由于指向数组的指针指向数组的第一个元素(具有相同的地址),我不明白为什么会发生这种情况:
#include <stdio.h>
int main(void) {
char (*t)[] = {"test text"};
printf("%s\n", *t + 1); // prints "est text"
}
另外,为什么下面的代码会打印
2
呢?#include <stdio.h>
int main(void) {
char (*t)[] = {1, 2, 3, 4, 5};
printf("%d\n", *t + 1); // prints "2"
}
最佳答案
在写这个答案的时候,所有其他的答案都是错误的。此外,你的问题闻起来像是一个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
的(不可变)数组的指针。然而,在您的例子中,使用指向不正确类型的指针并不重要,因为尽管这两个指针的类型不兼容,但它们指向相同的地址-仅,一个指向char数组,另一个指向char数组中的第一个字符;因此观察到的行为是相同的。当您取消引用指向-
t
数组的指针char
时,您将获得-char
数组的定位值(左值)。char数组的左值在正常情况下会衰减为指向第一个元素的指针(通常如此),因此*t + 1
现在将指向该数组中的第二个字符;然后printf
使用该值将从该指针开始打印以0结尾的字符串的内容。%s
的行为在C11(n1570)中规定为[抄送]
如果不存在长度修饰符,则参数应是指向
字符类型数组的元素。数组中的字符是
写入(但不包括)终止空字符。[…]如果
精度未指定或大于数组大小,数组应
包含空字符。[...]
(强调我的。)
至于第二次初始化:
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};
^
因此
%s
将从l
转换为指向-1
数组的指针,而无需任何转换。然后,在剩余的值中,编译器将抱怨:
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
当您取消引用这个值(它是指向char数组的指针)时,您将得到从内存地址1开始的char数组的左值。当您添加1时,这个char左值数组将衰减为指向char的指针,结果您将得到
int
这是指向-char
的指针,其值为((char*)1) + 1
。可根据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
。现在,将
2
作为参数传递给char *
,使用(char*)2
进行转换,这需要printf
。这具有未定义的行为;在您的情况下,%d
的字节模式被充分混淆地解释为int
并因此被打印。现在人们意识到打印的值与初始值设定项中的
(char*)2
无关:#include <stdio.h>
int main(void) {
char (*t2)[] = {1, 42};
printf("%d\n", *t2 + 1);
}
仍将打印
2
,而不是2
。量化宽松政策。或者,对于这两种初始化,您可以使用C99复合文本来初始化:
// Warning: this code is super *evil*
char (*t)[] = &(char []) { "test text" };
char (*t2)[] = &(char []) { 1, 2, 3, 4, 5 };
虽然这可能比你想要的更少,而且最终的代码在C89或C++编译器中没有任何编译的机会。
关于c - 为什么解引用指向字符串(char数组)的指针会返回整个字符串而不是第一个字符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39155012/