问题描述
出于好奇,我编写了一个非常简单程序,以读取单元素数组前后的前1000个字节,以查看要获得的值以及生成的值其中
#include <stdio.h>
int main(){
char mem[1];
printf("\n\tSeeking Ahead...\n\t %lld to %lld\n\n",mem,mem+1000);
for(int i=0; i <= 1000; i++)
printf("%lld ",mem[i]);
printf("\n\n\tSeeking Behind...\n\t %lld to %lld\n\n",mem-1000,mem);
for(int i=1000; i >= 0; i--;)
printf("%lld ",*(mem-i));
printf("\n\n-------END------\n\n");
return 0;
}
选择"%lld"
的原因是我有一个模糊的想法,即64位系统将具有64位地址,因此,长long可能是合适的(64位int)。
我不使用"%d"
是因为int太小会给我-ve值,并且"%x"
或"%o"
也会疯狂使用超出一个unsigned int
-例如,我会得到ffff...
,其中int
将读取-ve值。
我知道就C标准而言,这基本上是 U.B。,但是没有什么是真正随机的,因此我想知道为什么的可能原因:
- 某些值(如
127
,0
)重复出现 - 显示的大多数值都是8/10位数字,这些数字固定为
4294967...
- 一些看似随机的2或3位数字值在这大量数字之间浮动,例如
123
,18
,55
,96
...
我不是在问为什么这些 exact 值会出现,这是不可能回答的,我是在问为什么0
s的常规模式 ,出现8-10位数字(带有7个公共数字?)和一些正常的2-3位数字值,以及如何理解这些值?
还仅在MacOSX(尚未尝试Windows)上运行此程序,并使用"%c"
在' forwarding '时返回实际字符像这样:
executable_path=./memdump./memdumpTERM_PROGRAM=Apple_TerminalSHELL=/bin/bashTERM=xterm-256colorTMPDIR=/var/folders...
为什么?
解决方法
printf()
指示符%lld
期望以long long
作为参数。如果像您一样提供一个较短的变量,则只需设置此参数的一部分并导致UB,除了导致超出范围访问数组的UB之外(是的,我知道UB是UB,并且没有其他区别UB的形式,但我想解释一下为什么获得了您得到的值)。在AMD64上,该值的高32位可能与该程序的某些随机部分相同(之前设置了所用的寄存器),而char
参数仅更改了低32位。 int
范围之外的每个值都是由于此错误。
mem[i]
是一个字符,然后被提升为int
。因此,通常您不会以这种方式获得int
范围内的值,而不会得到char
范围内的值。
如果要进行该实验,请使用正确的格式说明符,我建议您使用十六进制格式说明符。使用unsigned char
也会更聪明。它将仍然是UB,因为它会无限制地访问内存,但是您更有可能打印出实际存储在内存中的内容。
您可以对[]
运算符使用负值,当指针指向数组的中间或结尾且负值仍在数组内部时,可以很好地定义此值。情况并非如此,因为它不再位于数组中,但仍然可以使用。您可以将两个循环组合成一个循环。