4 votes

Le passage d'une chaîne de caractères en ligne à un appel de sous-routine, où le paramètre a une longueur définie, donne des résultats inattendus.

J'ai constaté que ce code se comportait de manière inattendue

module testmodule
   integer, parameter :: LCHARS = 50
contains
   subroutine init()
      call foobar("foobar")
   end subroutine

   subroutine foobar(s)
      character(len=*), intent(in) :: s
      call bar(s)
   end subroutine

   subroutine bar(str)
      character(len=LCHARS), intent(in)  :: str

      print *, str
   end subroutine
end module

program foo
   use testmodule
   call init()
end program

Ce code imprime des déchets qui dépendent du compilateur.

Je pense que le problème réside dans le fait que je dois passer par une routine avec des len=* pour un argument de type chaîne, qui est ensuite transmis à une routine dont la longueur est spécifiée pour l'argument de type chaîne.

Que se passe-t-il exactement sous le capot, et où ce comportement est-il décrit dans la norme ? Devrais-je m'abstenir de spécifier la longueur des arguments des routines de caractères, puisque ce comportement peut se produire à tout moment sans avertissement ?

5voto

eriktous Points 4170

Je pense que votre code n'est pas conforme. Section 12.4.1.1 de la norme Fortran 95 :

12.4.1.1 Arguments réels associés à des objets de données fictifs
[...]
Si un argument fictif scalaire est de type caractère par défaut, la longueur len o doit être inférieure ou égale à la longueur de l'argument réel. L'argument fictif devient associé à l'argument le plus à gauche len caractères de l'argument réel.

3voto

Chris Points 12438

Le problème est que bar nécessite une chaîne de longueur 50 (cf. character(len=LCHARS), intent(in) :: str ), alors que la chaîne de caractères que vous lui passez n'est que de longueur 6. En compilant cela avec

ifort -Warn all,nodec,interfaces,declarations -gen_interfaces -check all -std test.f90

produit l'erreur

forrtl : sévère (408) : fort : (18) : La variable à caractère fictif "STR" a une longueur de 50 qui est supérieure à la longueur réelle de la variable 6.

Pour autant que l'on sache tous les arguments Fortran sont transmis par référence . En coulisses, quelle est la fonction bar obtient un pointeur sur le début de la chaîne de caractères str et un paramètre supplémentaire dont la valeur est la longueur de la chaîne. Ainsi, le bar occupera 50 caractères de mémoire, en commençant par le début de str et l'imprimer à l'écran. Étant donné que la chaîne que vous transmettez ne comporte que 6 caractères, les 44 caractères restants seront ceux qui se trouvent dans la partie de la mémoire qui suit "foobar", ce qui variera au moment de l'exécution ou en fonction du compilateur que vous utilisez.

2voto

janneb Points 17303

Le passage des arguments dépend du compilateur, pour autant que les exigences de la norme soient respectées, mais en général, un argument fictif CHARACTER(len=*) aura une interface quelque chose comme

void foo(char *s, int len)

et dans la mise en œuvre de la procédure foo, l'argument caché len est utilisé comme longueur de la chaîne. En revanche, pour un argument CHARACTER(len=valeur quelconque), l'argument len caché est ignoré ou n'est pas transmis du tout, et le code de la procédure suppose que la valeur quelconque est la longueur correcte de la chaîne de caractères.

Comme vous l'avez vu, vous ne devriez jamais utiliser autre chose que LEN=*, sauf si vous savez vraiment ce que vous faites et que vous pouvez citer un chapitre et un verset de la norme pour expliquer pourquoi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X