我是一个C语言的初学者,我想知道如果我写这样的东西会发生什么:
int *p; int b = 4; int a = 3; p = &a; printf("%d", p[1])
我期待的结果是“4”,然而,我得到了一个意外的结果(这是一个随机数)下面我也做实验:EXP1EXP2显示器这让我更困惑了。我想要一些解释,谢谢。
igetnqfo1#
p[1]指向p所指向的对象结尾之外。取消引用此指针是未定义的行为。
p[1]
p
falq053o2#
编译器不一定要按照定义对象的顺序存储对象。“如果你递给一个助手一本书、一个盒子和一个瓶子,你让他们把这些东西放在架子上,你认为他们一定会按照这个顺序把它们放在架子上吗?不,他们可能会先拿一个最容易操作的东西放在架子上,然后再放一个。”然后是另一个。如果他们不认为有任何意义,他们也不在乎你把物品交给他们的顺序。当你给编译器定义对象时,编译器会根据诸如大小和对齐要求之类的属性来管理它们。它不一定按照你定义它们的顺序来保存它们。它可能会在某些数据结构中按字母顺序来存储它们。或者它可能会按散列码的顺序来存储它们。当它开始给项目分配内存位置时,它可能会首先处理具有最严格对齐要求的对象,然后,在每个组中,按字母顺序或通过遍历其自身内部数据结构来检索它们。所以,b在内存中不一定在a之后。但是,即使是这样,C标准也没有定义当p指向a时访问p[1]的行为。这意味着编译器在分析代码的含义时,可以决定printf("%d", p[1])没有含义,并可以忽略它。因此,编译器优化可以以令人惊讶的方式转换程序。
b
a
printf("%d", p[1])
4ktjp1zp3#
一个有趣的事情是,当你使用clang来编译它时,你可以得到4(https://tio.run/##S9ZNzknMS///XzkzLzmnNCVVwaa 4JCUzXy/Djiszr 0 QhNzEzT 0 OTq 5 pLQQHE 1 SqwBrIg 7 CQFWwUTBDcRyDWGcAuATLVEKLsIKJmmoaSaoqSjUBBtGKtpzVX 7/z 8A),但是它仍然是一个未定义的行为,不应该像预期的那样使用。
3条答案
按热度按时间igetnqfo1#
p[1]
指向p
所指向的对象结尾之外。取消引用此指针是未定义的行为。falq053o2#
编译器不一定要按照定义对象的顺序存储对象。
“如果你递给一个助手一本书、一个盒子和一个瓶子,你让他们把这些东西放在架子上,你认为他们一定会按照这个顺序把它们放在架子上吗?不,他们可能会先拿一个最容易操作的东西放在架子上,然后再放一个。”然后是另一个。如果他们不认为有任何意义,他们也不在乎你把物品交给他们的顺序。
当你给编译器定义对象时,编译器会根据诸如大小和对齐要求之类的属性来管理它们。它不一定按照你定义它们的顺序来保存它们。它可能会在某些数据结构中按字母顺序来存储它们。或者它可能会按散列码的顺序来存储它们。当它开始给项目分配内存位置时,它可能会首先处理具有最严格对齐要求的对象,然后,在每个组中,按字母顺序或通过遍历其自身内部数据结构来检索它们。
所以,
b
在内存中不一定在a
之后。但是,即使是这样,C标准也没有定义当
p
指向a
时访问p[1]
的行为。这意味着编译器在分析代码的含义时,可以决定printf("%d", p[1])
没有含义,并可以忽略它。因此,编译器优化可以以令人惊讶的方式转换程序。4ktjp1zp3#
一个有趣的事情是,当你使用clang来编译它时,你可以得到4(https://tio.run/##S9ZNzknMS///XzkzLzmnNCVVwaa 4JCUzXy/Djiszr 0 QhNzEzT 0 OTq 5 pLQQHE 1 SqwBrIg 7 CQFWwUTBDcRyDWGcAuATLVEKLsIKJmmoaSaoqSjUBBtGKtpzVX 7/z 8A),但是它仍然是一个未定义的行为,不应该像预期的那样使用。