c - 如果我们将整数指针指向字符数组的开头并取消引用它会发生什么?
问题描述
#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 标准定义,因为使用*x
afterint *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。
总之,获得这个结果需要很多依赖于实现的行为:
s
为int
对象适当对齐。- C 实现支持
char
使用int
. - C 实现使用 ASCII 和八位字节。
int
在 C 实现中是四个字节。- C 实现
int
以小端顺序存储对象。
推荐阅读
- kubernetes - Kops API 需要什么语言?
- android - Android Oreo:有没有办法在手机重启时自动启动应用程序?
- android - Android Keystore 解密加密数据给出不正确的结果
- ios - 如何在 Google Maps iOS SDK 中获取当前相机位置的标记数据?
- python - 将 Python 脚本从 oauth2client 升级到 google-auth
- git - 如何撤消未提交工作上的意外 git stash pop
- java - Java 反射 - 未找到 Android 类
- python - 无法专注于卡车图像的车牌,并检索它以放入不同的文件夹
- bluetooth - 是否可以扫描蓝牙设备以查找其功能
- node.js - 将异步方法添加到 Mongoose 查找