数组和指针是完全不同的动物。在大多数情况下,指定数组的 表达式 被视为指针。
首先,一点标准语言 (n1256):
6.3.2.1 左值、数组和函数指示符
...
3 除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串文字,否则类型为“type 的数组”的表达式是转换为类型为“pointer to type”的表达式,该表达式指向数组对象的初始元素并且不是左值。如果数组对象有寄存器存储类,则行为未定义。
字符串文字“这是一个测试”是一个 15 元素数组 char。在声明中
char *string1 = "this is a test";
string1 被声明为指向
char 的指针。根据上述语言,
表达式“这是一个测试”的类型从
char [15] 转换为
char *,并将生成的指针值分配给
string1。
在声明中
char string2[] = "this is a test";
发生了一些不同的事情。更标准的语言:
6.7.8 初始化
...
14 字符类型的数组可以由字符串字面量初始化,可选
括在大括号中。字符串文字的连续字符(包括
如果有空间或数组大小未知,则终止空字符)初始化数组的元素。
...
22 如果一个未知大小的数组被初始化,它的大小由具有显式初始化器的最大索引元素确定。在其初始值设定项列表的末尾,数组不再具有不完整类型。
在这种情况下,string2 被声明为 char 的数组,它的大小是根据初始化器的长度计算的,并且字符串文字的 内容 被复制到大批。
这是一个假设的内存映射来说明正在发生的事情:
项目地址 0x00 0x01 0x02 0x03
---- -------- ---- ---- ---- ----
没有名字 0x08001230 't' 'h' 'i' 's'
0x08001234 ' ' '我' 's' ' '
0x08001238 'a' ' ' 't' 'e'
0x0800123C 's' 't' 0
...
字符串1 0x12340000 0x08 0x00 0x12 0x30
string2 0x12340004 't' 'h' 'i' 's'
0x12340008 ' ' '我' 's' ' '
0x1234000C 'a' ' ' 't' 'e'
0x1234000F 's' 't' 0
字符串字面量具有静态范围;也就是说,它们的内存在程序启动时被留出并一直保留到程序终止。尝试修改字符串文字的内容会调用未定义的行为;底层平台可能允许也可能不允许,标准对编译器没有限制。最好表现得好像文字总是不可写的。
在我上面的内存映射中,字符串文字的地址与string1 和string2 的地址有所不同,以说明这一点。
无论如何,您可以看到string1,具有指针类型,包含字符串文字的地址。 string2 是一个数组类型,包含字符串文字的 contents 的副本。
由于string2 的大小在编译时是已知的,sizeof 返回数组中的大小(字节数)。
%i 转换说明符不适用于size_t 类型的表达式。如果您使用 C99,请使用 %zu。在 C89 中,您将使用 %lu 并将表达式转换为 unsigned long:
C89: printf("%lu, %lu\n", (unsigned long) sizeof string1, (unsigned long) sizeof string2);
C99: printf("%zu, %zu\n", sizeof string1, sizeof string2);
注意sizeof是一个操作符,而不是一个函数调用;当操作数是一个表示 object 的表达式时,括号不是必需的(尽管它们不会造成伤害)。