繁体   English   中英

内存地址和内容

[英]Memory address and content

我已经写了

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

我希望在每个地址中看到一个字节。 但是,我看不到这样的东西。 为什么?

首先,指针算法和取消引用运算符支持数据类型。

请记住,一种指针算术(它比数组的最后一个元素生成一个指针)是有效的,但是尝试取消对生成的指针的引用是未定义的行为。

尝试取消引用指向无效内存位置的指针是未定义的行为

那就是

引用C11

一元*运算符表示间接。 [...]如果操作数的类型为类型的指针” ,则结果的类型为“ type”

因此,在你的情况, *(&a)是相同的a ,它的类型的int ,并且格式说明打印存储在整数值a

如果你想看到的逐字节的值,则需要将指针(地址投a )到char * ,然后,解引用指针看到存储在每个字节的值。

因此, (void*)(&a)+1应该更改为(char*)(&a)+1以指向内存的下一个字节。

您已经使用了格式指令%x %x需要一个unsigned int 您传递给printf的相应参数的类型不会改变它。 无论传递给它什么, printf都会从其参数读取一个unsigned int值。 如果您传递的内容与printf读取的内容不兼容,则程序的行为是不确定的。

您碰巧通过了一个int 一个int是不是一个unsigned int ,但它是足够接近,当你阅读int期待一个unsigned int的值保持不变,如果该整数为正数或零。 负整数映射到一个较大的正值( UINT_MAX - a在使用二进制补码表示的机器上,这几乎是所有机器)。

如果在希望获得int (是否带符号)时将char (是否带符号)传递给printf ,则由于C语言的另一个功能(即促销),该行为是明确定义的。 是小于整数类型的值int (即charshort )转换为int ¹。 因此,以下程序段定义明确,并在地址p处显示了一个字节的值(假设被一个有效的指针值代替):

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

*(&a)是一回事a 要只看到a一个字节,可以将指针&a unsigned char *转换为unsigned char *类型。

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

这将打印的代表性的一个字节a在内存中。 注意,表示形式取决于机器的字节序

您的代码段不会初始化a ,所以在第一次调用printf打印任何垃圾正好处于的位置, a在内存中在这个时候。 假设int没有任何陷阱表示 ,实际上在所有C实现中都是如此。

第二个调用printf尝试打印*(&a)+1 这只是a+1 您得到的输出令人惊讶:您确定实际上没有使用*(&a+1)运行程序吗? 这似乎是您想要探索的。 使用*(&a+1) ,您的程序的行为将是不确定的,因为它看起来比a过去一个int ,而a不在两个或多个int的数组中。 在实践中,您很可能会获得堆栈中低于a任何内容,但这并不是您可以依靠的。

如果要查看内存中a开头之后1处的字节值,则需要先将指针转换为字节指针。 将整数n添加到指针时,这不会将n添加到指针中存储的地址 ,而是将n添加到指针本身 这仅在指针指向数组内的值时才有用。 然后p + n指向n数组元素过去p 实际上, 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));

如果int至少由两个字节组成(在C语言中不是严格强制的,但在除少数嵌入式系统之外的所有情况下都是如此),这是定义良好的(但是具有实现定义的值,因为它取决于平台的字节序) )。

(void*)(&a) + 1不是标准C,因为void没有大小,因此进一步移动指针以void一个void元素void也没有意义。 但是,某些实现(例如GCC)将void*视为字节指针,就像unsigned char * ,因此向void*添加一个整数会将此整数添加到指针中存储的地址。

¹ 或如果较小的类型不适合带符号的int ,则为unsigned int ,例如在shortint具有相同大小的平台上。

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

  • 对指针使用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)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM