4

我发现这段代码行为异常

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

此代码打印依赖于编译器的垃圾。

我看到问题在于我正在跳过带有len=*字符串参数的例程,然后将其传递给字符串参数指定长度的例程。

幕后到底发生了什么,标准中在哪里描述了这种行为?我是否应该避免为字符例程参数指定长度,因为这种行为可能随时发生而没有警告?

4

3 回答 3

5

我认为您的代码不合格。Fortran 95 标准的部分12.4.1.1规定:

12.4.1.1 与虚拟数据对象相关的实际参数
[...]
如果标量虚拟参数是默认字符类型,则虚拟参数的长度len应小于或等于实际参数的长度。虚拟参数与实际参数最左边的len个字符相关联。

于 2012-01-17T13:19:56.367 回答
3

问题是它bar需要一个长度为 50 的字符串(参见character(len=LCHARS), intent(in) :: str),而您传递的字符串长度仅为 6。用

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

产生错误

forrtl:严重(408):fort:(18):虚拟字符变量“STR”的长度为 50,大于实际变量长度 6

据了解,所有 Fortran 参数都是通过引用传递的。在幕后,函数bar得到的是一个指向字符串开头的指针str和一个额外的参数,其值是字符串的长度。因此bar将占用 50 个字符的内存,从 的开头开始str,并将其打印到屏幕上。由于您传递的字符串只有 6 个字符长,剩余的 44 个字符将是“foobar”之后的下一位内存中的任何内容,这将在运行时或根据您使用的编译器而有所不同。

于 2012-01-17T12:55:46.900 回答
2

参数传递依赖于编译器,只要满足标准的要求,但通常,一个 CHARACTER(len=*) 虚拟参数将具有类似的接口

void foo(char *s, int len)

并且在 foo 过程的实现中,隐藏的 len 参数用作字符串长度。OTOH,对于 CHARACTER(len=somevalue) 参数,隐藏的 len 参数要么被忽略,要么根本不传递,并且该过程的代码假定 somevalue 是字符串的正确长度。

如您所见,除非您真的知道自己在做什么,并且可以引用标准中的章节来解释原因,否则您不应该使用除 LEN=* 之外的任何东西。

于 2012-01-17T13:00:58.973 回答