当传递字符串文字时,它是完全复制为 sizeof(myString) 的局部变量还是编译器“知道”通过引用传递它,因为在 C 中数组总是通过引用传递?
字符串字面量存储为一个数组,这样它就可以在程序的生命周期内使用,并且与任何其他数组表达式一样遵循相同的转换规则;也就是说,除非它是 sizeof 或一元 & 运算符的操作数,或者是用于在声明中初始化数组的字符串文字,否则类型为“T 的 N 元素数组”的表达式将被转换(“衰减”)为“指向T”类型的表达式,表达式的值将是数组中第一个元素的地址。因此,在通话中
myFunction( mystring );
和
myFunction( "A string" );
两个参数都是数组类型的表达式,sizeof 或一元 & 运算符的操作数都不是,因此在这两种情况下,表达式都衰减为指向第一个元素的指针。就函数调用而言,两者完全没有区别。
让我们看一个真实的例子(SLES 10、x86_64、gcc 4.1.2)
#include <stdio.h>
void myFunction( const char *str )
{
printf( "str = %p\n", (void *) str );
printf( "str = %s\n", str );
}
int main( void )
{
static const char mystring[] = "A string";
myFunction( mystring );
myFunction( "A string" );
return 0;
}
myFunction 打印出字符串文字和mystring 变量的地址和内容。结果如下:
[fbgo448@n9dvap997]~/prototypes/literal: gcc -o literal -std=c99 -pedantic -Wall -Werror literal.c
[fbgo448@n9dvap997]~/prototypes/literal: ./literal
str = 0x400654
str = A string
str = 0x40065d
str = A string
字符串文字和mystring 数组都存储在可执行文件的.rodata(只读)部分中:
[fbgo448@n9dvap997]~/prototypes/literal: objdump -s literal
...
Contents of section .rodata:
40063c 01000200 73747220 3d202570 0a007374 ....str = %p..st
40064c 72203d20 25730a00 41207374 72696e67 r = %s..A string
40065c 00412073 7472696e 6700 .A string.
...
mystring 声明中的static 关键字告诉编译器mystring 的内存应该在程序启动时留出并保留到程序终止。 const 关键字表示内存不应该被代码修改。在这种情况下,将其粘贴在 .rodata 部分非常有意义。
这意味着在运行时不会为mystring 分配额外的内存;它已经作为图像的一部分分配。在这种特殊情况下,对于这个特殊的平台,使用其中一个或另一个之间没有绝对没有区别。
如果我不将mystring声明为static,如
int main( void )
{
const char mystring[] = "A string";
...
然后我们得到:
str = 0x7fff2fe49110
str = A string
str = 0x400674
str = A string
意味着只有字符串文字被存储在.rodata:
Contents of section .rodata:
40065c 01000200 73747220 3d202570 0a007374 ....str = %p..st
40066c 72203d20 25730a00 41207374 72696e67 r = %s..A string
40067c 00 .
因为它被声明为main 的本地并且不 声明为static,所以mystring 被分配了auto 存储持续时间;在这种情况下,这意味着内存将在运行时从堆栈中分配,并将在mystring 的封闭范围(即main 函数)的持续时间内保持。作为声明的一部分,字符串文字的内容将被复制到数组中。由于它是从堆栈分配的,因此数组原则上是可修改的,但 const 关键字告诉编译器拒绝任何试图修改它的代码。