如果我们将整数指针指向字符数组的开头并取消引用它,会发生什么


#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实现支持使用intchar对象进行混叠
  • C实现使用ASCII和八位字节
  • CCD_ 35在C实现中是四个字节
  • C实现以小端序存储int对象

最新更新