代码之家  ›  专栏  ›  技术社区  ›  mahmood

内存地址和内容

  •  -1
  • mahmood  · 技术社区  · 6 年前

    int a;
    printf("addr = %p and content = %x\n", (void*)(&a), *(&a));
    printf("addr = %p and content = %x\n", (void*)(&a)+1, *(&a)+1);
    

    我在输出中看到的是

    addr = 0x7fffffffde3a and content = 55554810
    addr = 0x7fffffffde3b and content = 5555
    

    我希望每个地址有一个字节。不过,我看不出这样的事。为什么?

    3 回复  |  直到 6 年前
        1
  •  2
  •   Sourav Ghosh    6 年前

    首先,指针算法和解引用运算符遵循数据类型。

    记住,指针算法是有效的,它生成一个超过数组最后一个元素的指针,但是试图取消对生成指针的引用是未定义的行为。

    undefined behavior .

    也就是说,

    引用 C11

    一元 * 指向类型的指针 ,结果具有类型 类型 .

    *(&a) a ,类型为 int ,格式说明符打印存储在 .

    如果你想看 逐字节 )给一个 char * 然后,解引用指针以查看每个字节中存储的值。

    (void*)(&a)+1 应该改成 (char*)(&a)+1 指向内存的下一个字节。

        2
  •  0
  •   SKi    6 年前

    如果要打印字节,请播放字节:

    • 使用 unsigned char* uint8_t*
    • 使用%hhx告诉printf输入值是字符长度。

    int a = 0x12345678;
    
    printf("addr = %p and content = %hhx\n", (void*)&a,     *(uint8_t*)&a);
    printf("addr = %p and content = %hhx\n", ((void*)&a)+1, *((uint8_t*)&a+1));
    

    在我的小恩迪亚环境:

    addr = 0xbedbacac and content = 78
    addr = 0xbedbacad and content = 56
    

    *((uint8_t*)&a+1) 而不是 *(uint8_t*)&a+1 . 在这个例子中,后者将返回79(78+1)。

        3
  •  0
  •   Gilles 'SO- stop being evil'    6 年前

    您使用了格式化指令 %x %十 unsigned int . 作为相应参数传递给的类型 printf 不会改变的。 打印 将读取 它的参数中的值,无论传递给它什么。如果你传递的东西与 读取时,程序的行为未定义。

    你碰巧通过了 int 内景 不是 ,但当你读到 内景 无符号整型 ,如果整数为正或零,则值保持不变。负整数映射到一个大正值( UINT_MAX - a 在使用2的补码表示的机器上,这几乎是所有的机器)。

    如果你通过了 char (是否签名)至 打印 内景 (不管是否签名),由于C语言的另一个特性,即提升,行为定义良好。整数类型的值小于 内景 烧焦 short 内景 . 因此,下面的程序片段定义良好,并在地址处打印一个字节的值 p (假设 …

    unsigned char p = …;
    printf("addr = %p and content = %x\n", (void*)p, *p);
    

    *(&a) 是同一件事 a . 只看到一个字节 ,你可以投下指针 &a unsigned char * .

    printf("addr = %p and content = %x\n", (void*)&a, *(unsigned char *)(&a));
    

    这将打印 endianness .

    ,所以第一次打电话给 打印 打印出发生在 内景 trap representation ,实际上所有C实现都是这样。

    第二次呼叫 尝试打印 *(&a)+1 a+1 . 你得到的结果令人惊讶:你确定你没有真正用 *(&a+1) ? 这似乎是你想要探索的。与 *(&a+1) ,程序的行为将是未定义的,因为 过去的 不在两个或多个数组中 内景 但那不是你能指望的。

    在内存中,首先需要将指针强制转换为字节指针。当您添加整数时 对于指针,这不会添加 n个 地址 存储在指针中,它添加 指针本身 . 这只在指针指向数组中的值时才有用;然后 p + n n 数组元素过去 第页 p[n] 相当于 *(p+n) . 如果要将1添加到地址,则需要获取一个字节指针,即指向 unsigned char

    int a[2] = {0x12345678, 0x9abcdef0};
    printf("addr = %p and content = %x\n", (unsigned char*)(&a) + 1, *((unsigned char*)(&a) + 1));
    printf("addr = %p and content = %x\n", (&a) + 1, *((&a) + 1));
    

    这是定义良好的(但是有一个实现定义的值,因为它取决于平台的端性),前提是 至少由两个字节组成(这在C中不是严格的强制要求,但除了少数嵌入式系统之外,其他地方都是这样)。

    (void*)(&a) + 1 不是标准C,因为 void 没有大小,因此将指针移动到 元素进一步。但是有些实现,比如GCC treat void* 作为字节指针,就像 ,因此将一个整数添加到 空的* 将此整数添加到指针中存储的地址。

    或者去 无符号整型 如果较小的类型不适合签名 ,例如,在一个平台上 内景 有同样的尺寸。