在撰写此答案时,所有其他答案都不正确。此外,您的问题闻起来像an XY problem,因为您尝试的构造很可能不是您想要的。你真正想做的只是:
char *t = "test text";
printf("%s\n", t); // prints "test text"
或
printf("%c\n", t[1]); // prints "e", the 2nd character in the string.
但既然你想了解为什么会发生这些事情,而所有其他解释都是错误的,那么这里是:
您的声明将 t 声明为指向 char 数组的指针:
cdecl> explain char (*t)[];
declare t as pointer to array of char
不是其他人建议的指针数组。此外,*t 的类型不完整,因此您无法获取其大小:
sizeof *t;
会导致
error: invalid application of ‘sizeof’ to incomplete type ‘char[]’
sizeof *t;
在编译时。
现在,当你尝试用
初始化它时
char (*t)[] = {"test text"};
它会发出警告,因为虽然"test text" 是一个由(常量)char 组成的数组,但这里它会衰减到一个指向char 的指针。此外,那里的牙套也没用;上面的摘录等于写:
char (*t)[] = "test text";
不一样
int a = 42;
和
int a = {42};
是同义词。这是C。
要获得指向数组的指针,您必须在数组(字符串字面量!)上使用“address-of”运算符,以避免它衰减为指针:
char (*t)[] = &"test text";
现在t 被正确初始化为指向char 的(不可变)数组的指针。但是,在您的情况下,使用指向不正确类型的指针并不重要,因为这两个指针尽管是不兼容的类型,但指向相同的地址 - 只有一个指向字符数组,另一个指向第一个字符在那个字符数组中;因此观察到的行为是相同的。
当您取消引用t(指向char 的数组的指针)时,您将获得数组char 的定位器值(左值)。然后,在正常情况下,char 数组的左值将衰减为指向第一个元素的指针,就像他们通常做的那样,所以 *t + 1 现在将指向该数组中的第二个字符;然后printfing 该值将打印一个以 0 结尾的字符串的内容从该指针开始。
%s 的行为在 C11 (n1570) 中指定为
[%s]
如果不存在l 长度修饰符,则参数应为指向初始值的指针
字符类型数组的元素。 数组中的字符是
写到(但不包括)终止空字符。 [...] 如果
精度未指定或大于数组的大小,数组应
包含一个空字符。 [...]
(强调我的。)
至于你的第二次初始化:
char (*t2)[] = {1, 2, 3, 4, 5};
如果你使用最新版本的 GCC 编译它,默认情况下你会收到很多警告,首先:
test.c:10:19: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
char (*t2)[] = {1, 2, 3, 4, 5};
^
因此,1 从 int 转换为指向数组的指针char 没有任何强制转换。
然后,在剩下的值中,编译器会报错:
y.c:10:19: note: (near initialization for ‘t2’)
y.c:10:21: warning: excess elements in scalar initializer
char (*t2)[] = {1, 2, 3, 4, 5};
^
也就是说,在您的情况下,2、3、4 和 5 被默默地忽略了。
因此,该指针的值现在为 1,例如在 x86 平面内存模型上,它将指向内存位置 1(尽管这自然是实现定义的):
printf("%p\n", (void*)t2);
打印(定义双重实现)
0x1
当你取消引用这个值(它是一个指向字符数组的指针)时,你会得到一个字符数组的左值,它从内存地址 1 开始。当你加 1 时,这个 array-of-char 左值将衰减为指向字符的指针,因此您将得到((char*)1) + 1,它是指向char 的指针,其值为2。该值的类型可以从GCC(5.4.0)默认生成的警告中验证:
y.c:5:10: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘char *’ [-Wformat=]
printf("%d\n",*t2+1); //prints "2"
^
参数的类型是char *。
现在您将(char*)2 作为参数传递给printf,以使用%d 进行转换,它需要int。这具有未定义的行为;在您的情况下,(char*)2 的字节模式被充分混淆地解释为2,因此它被打印出来。
现在人们意识到打印的值与原始初始化程序中的2 无关:
#include <stdio.h>
int main(void) {
char (*t2)[] = {1, 42};
printf("%d\n", *t2 + 1);
}
仍将打印2,而不是42。 QED。
对于这两种初始化,您可以使用 C99 复合文字来初始化:
// Warning: this code is super *evil*
char (*t)[] = &(char []) { "test text" };
char (*t2)[] = &(char []) { 1, 2, 3, 4, 5 };
虽然这可能会比您想要的更少,而且生成的代码没有任何机会在 C89 或 C++ 编译器中编译。