在C语言中,可以在这样的声明中使用字符串字面值:

char s[] = "hello";

或者像这样:

char *s = "hello";

那么有什么不同呢?我想知道在编译和运行时,在存储持续时间方面实际发生了什么。


当前回答

举个例子来说明区别:

printf("hello" + 2); //llo
char a[] = "hello" + 2; //error

在第一种情况下,指针算术是有效的(传递给函数的数组衰减为指针)。

其他回答

首先,在函数参数中,它们是完全等价的:

void foo(char *x);
void foo(char x[]); // exactly the same in all respects

在其他上下文中,char *分配一个指针,而char[]分配一个数组。你会问,在前一种情况下,弦在哪里?编译器秘密地分配一个静态匿名数组来保存字符串字面值。所以:

char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;

注意,你不能试图通过这个指针修改这个匿名数组的内容;效果是未定义的(通常意味着崩溃):

x[1] = 'O'; // BAD. DON'T DO THIS.

使用数组语法直接将其分配到新的内存中。因此修改是安全的:

char x[] = "Foo";
x[1] = 'O'; // No problem.

然而,数组只在其包含范围内存在,所以如果在函数中这样做,不要返回或泄漏指向该数组的指针——而是使用strdup()或类似的方法进行复制。如果数组是在全局范围内分配的,当然没有问题。

此声明:

char s[] = "hello";

Creates one object - a char array of size 6, called s, initialised with the values 'h', 'e', 'l', 'l', 'o', '\0'. Where this array is allocated in memory, and how long it lives for, depends on where the declaration appears. If the declaration is within a function, it will live until the end of the block that it is declared in, and almost certainly be allocated on the stack; if it's outside a function, it will probably be stored within an "initialised data segment" that is loaded from the executable file into writeable memory when the program is run.

另一方面,这个声明:

char *s ="hello";

创建两个对象:

一个6个字符的只读数组,包含值'h', 'e', 'l', 'l', 'o', '\0',它没有名称,具有静态存储持续时间(意味着它存在于程序的整个生命周期中);而且 一个类型为指针到字符的变量,称为s,用该未命名只读数组中第一个字符的位置初始化。

未命名的只读数组通常位于程序的“文本”段中,这意味着它与代码本身一起从磁盘加载到只读内存中。s指针变量在内存中的位置取决于声明出现的位置(就像在第一个例子中一样)。

char *s1 = "Hello world"; // Points to fixed character string which is not allowed to modify
char s2[] = "Hello world"; // As good as fixed array of characters in string so allowed to modify

// s1[0] = 'J'; // Illegal
s2[0] = 'J'; // Legal
char s[] = "hello";

声明s为一个char数组,其长度足以容纳初始化式(5 + 1个字符),并通过将给定字符串文字的成员复制到数组中来初始化数组。

char *s = "hello";

声明为指向一个或多个(在本例中是多个)字符的指针,并将其直接指向一个包含文字“hello”的固定(只读)位置。

c99n1256草案

字符串字面量有两种不同的用法:

Initialize char[]: char c[] = "abc"; This is "more magic", and described at 6.7.8/14 "Initialization": An array of character type may be initialized by a character string literal, optionally enclosed in braces. Successive characters of the character string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array. So this is just a shortcut for: char c[] = {'a', 'b', 'c', '\0'}; Like any other regular array, c can be modified. Everywhere else: it generates an: unnamed array of char What is the type of string literals in C and C++? with static storage that gives UB if modified So when you write: char *c = "abc"; This is similar to: /* __unnamed is magic because modifying it gives UB. */ static char __unnamed[] = "abc"; char *c = __unnamed; Note the implicit cast from char[] to char *, which is always legal. Then if you modify c[0], you also modify __unnamed, which is UB. This is documented at 6.4.5 "String literals": 5 In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence [...] 6 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

6.7.8/32“初始化”给出了一个直接的例子:

EXAMPLE 8: The declaration char s[] = "abc", t[3] = "abc"; defines "plain" char array objects s and t whose elements are initialized with character string literals. This declaration is identical to char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' }; The contents of the arrays are modifiable. On the other hand, the declaration char *p = "abc"; defines p with type "pointer to char" and initializes it to point to an object with type "array of char" with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to modify the contents of the array, the behavior is undefined.

GCC 4.8 x86-64 ELF实现

计划:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

编译和反编译:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

输出包含:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

结论:GCC将char* it存储在.rodata部分,而不是在.text中。

但是请注意,默认的链接器脚本将.rodata和.text放在同一个段中,该段有执行权限,但没有写权限。这可以观察到:

readelf -l a.out

它包含:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

如果我们对char[]做同样的操作:

 char s[] = "abc";

我们获得:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

因此它被存储在堆栈中(相对于%rbp)。