#include <stdio.h>
int main()
{
char s[]="ABCD EFGH";
int *x=&s[0];
printf("%d %dn", *x, x);
x=s;
printf("%d %dn", *x, x);
printf("%d %d %dn", &s[0], s, &s);
}
我可以了解一下这个代码输出的解释吗?
此代码的行为不是由C标准定义的,因为在int *x=&s[0];
之后使用*x
违反了C标准中的规则。C 2018 6.5 7说:
对象的存储值只能由具有以下类型之一的左值表达式访问:
--与对象的有效类型兼容的类型
--与对象的有效类型兼容的类型的合格版本,
--一种类型,是与对象的有效类型相对应的有符号或无符号类型
--一种类型,是与对象的有效类型的合格版本相对应的有符号或无符号类型
--在其成员中包括上述类型之一的聚合或并集类型(递归地包括子聚合或包含并集的成员(,或
--字符类型。
*x
尝试访问s
的字节,就好像它们是int
对象一样。然而,该内存的有效类型是char
(C 2018 6.5 6:"访问其存储值的对象的有效类型是对象的声明类型,如果有的话…"(。因此:
int
与有效类型char
不兼容int
不是与char
兼容的类型的限定版本- CCD_ 11不是对应于CCD_ 12的有符号或无符号类型
- CCD_ 13不是与CCD_ 14的限定版本相对应的有符号或无符号类型
int
不是任何类型的聚合或并集类型int
不是字符类型
违反此要求意味着未定义行为,根据C 2018 4 2:
如果;应";或";不得";出现在约束或运行时约束之外的要求被违反,行为未定义…
由于C标准未定义行为,编译器可以选择定义他们将如何处理它,也可以不定义他们将要如何处理它。在后一种情况下,编译器的优化可能会导致程序产生令人惊讶的结果。在前一种情况下,编译器的一种常见行为是通过将其指向的字节重新解释为int
类型来评估*x
,前提是int
对象的地址适当对齐。(GCC和Clang通过命令行开关-fno-strict-aliasing
允许这样做。(
也就是说,如果&s[0]
是在C实现中允许int
开始的地址,则*x
将从&s[0]
开始的字节产生int
值。如果C实现使用ASCII,则s
的前四个字节分别为4116、42116,4316+sub>和4416,sub>。然后,如果int
是四个八位字节存储的小端序(在较低地址具有较低有效字节(,并且s
对于int
是可接受的对齐,则*x
的值将是4443424116,十进制为1145258561。
总之,要得到这个结果需要大量依赖于实现的行为:
s
被适当地对准用于int
对象- C实现支持使用
int
对char
对象进行混叠 - C实现使用ASCII和八位字节
- CCD_ 35在C实现中是四个字节
- C实现以小端序存储
int
对象