首页 > 解决方案 > 如果我们将整数指针指向字符数组的开头并取消引用它会发生什么?

问题描述

#include <stdio.h>
int main()
{
    char s[]="ABCD EFGH";
    int *x=&s[0];
    printf("%d %d\n", *x, x);
    x=s;
    printf("%d %d\n", *x, x);
    printf("%d %d %d\n", &s[0], s, &s);
}

我可以了解一下这段代码输出的解释吗?

标签: c

解决方案


此代码的行为未由 C 标准定义,因为使用*xafterint *x=&s[0];违反了 C 标准中的规则。C 2018 6.5 7 说:

对象的存储值只能由具有以下类型之一的左值表达式访问:

— 与对象的有效类型兼容的类型,

— 与对象的有效类型兼容的类型的限定版本,

— 与对象的有效类型相对应的有符号或无符号类型,

— 对应于对象有效类型的限定版本的有符号或无符号类型,

— 在其成员中包含上述类型之一的聚合或联合类型(递归地,包括子聚合或包含联合的成员),或

— 一种字符类型。

*x尝试访问 的字节,s就好像它们是一个int对象一样。但是,此内存的有效类型是char(C 2018 6.5 6:“访问其存储值的对象的有效类型是对象的声明类型,如果有的话……”)。所以:

  • int与有效类型不兼容,char.
  • int不是与 兼容的类型的合格版本char
  • int不是对应于 的有符号或无符号类型char
  • int不是对应于 . 的合格版本的有符号或无符号类型char
  • int不是任何类型的聚合或联合类型。
  • int不是字符类型。

违反此要求意味着未定义行为,根据 C 2018 4 2:

如果违反了出现在约束或运行时约束之外的“应”或“不应”要求,则行为未定义……</p>

由于 C 标准未定义该行为,因此编译器可以选择定义他们将如何使用它或不定义他们将如何使用它。在后一种情况下,编译器的优化会导致程序产生令人惊讶的结果。在前一种情况下,编译器的常见行为是*x通过将其指向的字节重新解释为int类型来进行评估,前提是地址与对象适当对齐int。(GCC 和 Clang 允许使用命令行开关进行此操作-fno-strict-aliasing。)

也就是说,如果&s[0]是一个int在 C 实现中允许开始的地址,那么*x将产生一个int取自从 开始的字节的值&s[0]。如果 C 实现使用 ASCII,则前四个字节s是 41 16、 42 16、 43 16和 44 16。然后,如果int是四个 8 位字节存储的小端(在较低地址处具有较低有效字节),并且与s一致int,那么 的值*x将是 44434241 16,即十进制的 1,145,258,561。

总之,获得这个结果需要很多依赖于实现的行为:

  • sint对象适当对齐。
  • C 实现支持char使用int.
  • C 实现使用 ASCII 和八位字节。
  • int在 C 实现中是四个字节。
  • C 实现int以小端顺序存储对象。

推荐阅读