理解内存地址的值

Making Sense Of Values At Memory Addresses

出于好奇,我写了一个非常简单的程序来读取单元素数组前后的前 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 long 可能是合适的(64 位 int)。

我没有使用 "%d" 因为 int 太小会给我 -ve 个值,而且 "%x""%o" 也会因超出unsigned int 的大小 - 例如,我会得到 ffff...,其中 int 会读取 -ve 值。

我知道这基本上是 U.B. 就 C 标准而言,但没有什么是真正随机的,所以我想知道可能原因:

我不是在问为什么会出现这些 exact 值,那是不可能回答的,我是在问为什么 general pattern0s,8-10 位数字(有 7 个普通数字?)和一些看起来正常的 2-3 位数字值出现,还有 如何理解这些值?

此外, 运行 在 MacOSX 上使用此功能(尚未尝试 Windows),并使用 "%c",在 ' 求转发',它returns实际字符是这样的:

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,因为它越界访问内存,但您更有可能打印内存中实际存储的内容。

您可以为 [] 运算符使用负值,当您有一个指针指向数组的中间或末尾并且负值仍在数组内时,这是明确定义的。它不是你的情况,因为它不再在数组内但它仍然有效。您可以将两个循环合并为一个循环。