[英]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
(即char
和short
)转换为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
,例如在short
和int
具有相同大小的平台上。
如果要打印字节,请使用字节播放:
unsigned char*
或uint8_t*
例:
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.