【发布时间】:2020-10-10 08:24:12
【问题描述】:
我是这个论坛的新手,所以如果有任何令人震惊的格式选择,请告诉我,我会及时更新。
在C Programming: A Modern Approach(由 K.N. King 撰写)一书中,写了以下段落:
如果指针变量
p尚未初始化,尝试以任何方式使用p的值会导致未定义行为。在下面的例子中,printf的调用可能会打印垃圾,导致程序崩溃,或者有一些其他影响:int *p; printf("%d", *p);
据我了解指针以及编译器如何处理它们,声明 int *p 有效地说,“嘿,如果您将来取消引用 p,我将查看内存中四个连续字节的块,其起始地址是 p 中包含的值,并将这 4 个字节解释为有符号整数。"
至于是否正确……如果正确,那我有点疑惑为什么上面的代码块:
- 被归类为未定义行为
- 可能导致程序崩溃
- 可以有其他效果
对上述案例的评论:
我对未定义行为的理解是,在运行时,任何事情都可能发生。话虽如此,在我看来,在上面的代码中,只有非常明确的 子集 会发生。我知道p(由于缺乏初始化)正在存储一个随机地址,该地址可以指向内存中的任何位置。但是,当 printf 传递了取消引用的值 *p 时,编译器不会只查看内存的 4 个连续字节(从任意随机地址开始)并将这 4 个字节解释为有符号整数吗?
因此,printf 应该只做一件事:打印一个介于 -2,147,483,648 到 2,147,483,647 之间的数字。显然,这是很多不同可能的输出,但这真的可以称为“未定义的行为”。此外,这种“未定义的行为”如何导致“程序崩溃”或“产生其他影响”。
任何澄清将不胜感激!谢谢!
【问题讨论】:
-
"我将查看内存中四个连续字节的块" --> 不。UB 可以通过访问统一指针来发生 - 在调用
printf()之前。p未定义为对随机地址进行排序。 -
“任何事情都可能发生”部分通常是因为允许编译器假设导致 UB 的事情不可能发生。因此,如果您在
if条件中有 UB,编译器可能会忽略整个分支。 -
这也可能是非常有用的阅读:stackoverflow.com/questions/2397984/….
-
虽然“任何事情都可能发生”是对后果的唯一正确描述,但您可能需要考虑如果
p的未初始化内容是一个不是有效地址的值会发生什么整数,要么是因为内存不存在,要么是因为机器体系结构只允许整数具有某些地址(例如,可被 4 整除),或者因为该部分地址空间存在但不允许您的进程读取它等等等等。 -
或者
p中的地址恰好是内存映射I/O 控制器的一部分,并且从该地址读取被视为对硬盘驱动器重新格式化自身的命令。 (一些处理器,如 6502,通过内存映射地址执行所有 I/O。简单地从地址读取被认为是硬件命令。即使 x86 对某些硬件也使用锁存寻址,例如 EGA 视频卡。从锁存器读取会弄乱你的视频。)
标签: c pointers printf language-lawyer undefined-behavior