Cod*_*ion 0 c arrays string pointers pointer-arithmetic
由于指向数组的指针指向数组的第一个元素(具有相同的地址),我不明白为什么会发生这种情况:
#include <stdio.h>
int main(void) {
char (*t)[] = {"test text"};
printf("%s\n", *t + 1); // prints "est text"
}
Run Code Online (Sandbox Code Playgroud)
另外,为什么打印下面的代码2呢?
#include <stdio.h>
int main(void) {
char (*t)[] = {1, 2, 3, 4, 5};
printf("%d\n", *t + 1); // prints "2"
}
Run Code Online (Sandbox Code Playgroud)
写这个答案的那一刻的所有其他答案都是不正确的.此外,你的问题闻起来像一个 XY问题,因为你最有可能尝试的结构不是你想要的.你真正想要做的只是:
char *t = "test text";
printf("%s\n", t); // prints "test text"
Run Code Online (Sandbox Code Playgroud)
要么
printf("%c\n", t[1]); // prints "e", the 2nd character in the string.
Run Code Online (Sandbox Code Playgroud)
但既然你想了解为什么会发生这些事情,并且所有其他解释都是错误的,那么这里是:
您的声明声明t为指向char数组的指针:
cdecl> explain char (*t)[];
declare t as pointer to array of char
Run Code Online (Sandbox Code Playgroud)
不是其他人建议的指针数组.此外,类型*t不完整,所以你不能采取它的大小:
sizeof *t;
Run Code Online (Sandbox Code Playgroud)
会导致
error: invalid application of ‘sizeof’ to incomplete type ‘char[]’
sizeof *t;
Run Code Online (Sandbox Code Playgroud)
在编译时.
现在,当您尝试使用初始化时
char (*t)[] = {"test text"};
Run Code Online (Sandbox Code Playgroud)
它会发出警告,因为while "test text"是一个(常量)数组char,这里它会衰减到指针char.另外,那里的牙套没用; 以上摘录等同于写作:
char (*t)[] = "test text";
Run Code Online (Sandbox Code Playgroud)
不像
int a = 42;
Run Code Online (Sandbox Code Playgroud)
和
int a = {42};
Run Code Online (Sandbox Code Playgroud)
是同义词.这是C.
要获得指向数组的指针,必须在数组上使用"address-of"运算符(字符串文字!),以避免它衰减到指针:
char (*t)[] = &"test text";
Run Code Online (Sandbox Code Playgroud)
现在t是一个正确初始化为指向(不可变)数组的指针char.但是在你的情况下使用指向不正确类型的指针并不重要,因为2指针虽然是不兼容的类型,却指向同样的地址 - 只有一个指向char-array,另一个指向第一个字符在那个char数组中; 因此观察到的行为是相同的.
当您取消引用t(指向数组的指针)时char,您将获得数组的定位符值(左值)char.然后,正常情况下,一个字符数组的左值会像往常一样衰减到指向第一个元素的指针,因此*t + 1现在将指向该数组中的第二个字符; 然后printf,该值将打印从该指针开始的以0结尾的字符串的内容.
行为%s在C11(n1570)中指定为
[
%s]如果不存在
l长度修饰符,则参数应为指向字符类型数组的初始元素的指针.数组中的字符被写入(但不包括)终止空字符. [...]如果未指定精度或大于数组的大小,则数组应包含空字符.[...]
(强调我的.)
至于你的第二次初始化:
char (*t2)[] = {1, 2, 3, 4, 5};
Run Code Online (Sandbox Code Playgroud)
如果您使用最新版本的GCC编译它,默认情况下会收到很多警告,首先:
test.c:10:19: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
char (*t2)[] = {1, 2, 3, 4, 5};
^
Run Code Online (Sandbox Code Playgroud)
因此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};
^
Run Code Online (Sandbox Code Playgroud)
也就是说,在你的情况下,2,3,4和5被默默地忽略了.
因此,该指针的值现在为1,例如在x86平面内存模型上,它将指向内存位置1(尽管这自然是实现定义的):
printf("%p\n", (void*)t2);
Run Code Online (Sandbox Code Playgroud)
打印(双重实现定义)
0x1
Run Code Online (Sandbox Code Playgroud)
当你取消引用这个值(这是一个指向char数组的指针)时,你将获得一个从内存地址1开始的char-array的左值.当你加1时,这个char-number数组值将衰减到指向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"
^
Run Code Online (Sandbox Code Playgroud)
参数是类型的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);
}
Run Code Online (Sandbox Code Playgroud)
仍然会打印2,而不是42.QED.
或者对于两个初始化,您可以使用C99复合文字来初始化:
// Warning: this code is super *evil*
char (*t)[] = &(char []) { "test text" };
char (*t2)[] = &(char []) { 1, 2, 3, 4, 5 };
Run Code Online (Sandbox Code Playgroud)
虽然这可能比你想要的更少,但结果代码没有任何机会在C89或C++编译器中编译.
| 归档时间: |
|
| 查看次数: |
4414 次 |
| 最近记录: |