内存地址和内容

时间:2018-07-06 06:35:56

标签: c pointers

我写了

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 个答案:

答案 0 :(得分:2)

首先,指针算术和解引用运算符支持数据类型。

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

试图取消引用指向无效内存位置的指针为undefined behavior

说,

引用C11

  

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

因此,在您的情况下,*(&a)与类型为a的{​​{1}}相同,格式说明符将打印存储在int中的整数值。

如果您要查看个字节值,则需要将指针(地址a强制转换为a,然后取消引用指针,以查看每个字节中存储的值。

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

答案 1 :(得分:0)

您已经使用了格式指令%x%x期望有unsigned int。您作为printf的相应参数传递的内容的类型不会对此进行更改。 printf将从其参数中读取unsigned int的值,无论您传递给它什么。如果您传递的内容与printf所读取的内容不兼容,则程序的行为是不确定的。

您碰巧通过了intint不是unsigned int,但是它足够接近,以至于当您读取期望int的{​​{1}}时,如果整数为正或零,则该值保持不变。负整数映射到一个较大的正值(在使用二进制补码表示的机器上,unsigned int几乎是所有机器)。

如果在期望UINT_MAX - a(是否签名)的情况下将char(是否签名)传递给printf,则该行为的定义很明确,这是因为C语言,这是促销活动。小于int(即intchar)的整数类型的值将转换为short¹。因此,以下程序段定义明确,并在地址int上打印了一个字节的值(假设p被有效的指针值替换了):

unsigned char p = …; printf("addr = %p and content = %x\n", (void*)p, *p); *(&a)相同。要仅查看a的一个字节,可以将指针a转换为类型&a

unsigned char *

这将在内存中打印printf("addr = %p and content = %x\n", (void*)&a, *(unsigned char *)(&a)); 表示形式的一个字节。请注意,表示形式取决于机器的endianness

您的代码段未初始化a,因此对a的第一次调用将打印此时内存中printf所在位置的所有垃圾。假设a没有任何trap representation,几乎所有C实现都是这种情况。

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

如果要查看内存中a开头的1处的字节值,则需要先将指针强制转换为字节指针。当您将整数 n 添加到指针时,这不会将 n 添加到指针中存储的地址中,而是添加了 n 指向指针本身。这仅在指针指向数组内的值时才有用。然后a指向p + n之后的n个数组元素。实际上,p等效于p[n]。如果要将地址加1,则需要获取一个字节指针,即指向*(p+n)的指针。对比:

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中不是严格强制的),则定义明确(但有实现定义的值,因为它取决于平台的字节序)除了少数嵌入式系统之外,其他任何地方都可以。)

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

¹或如果较小的类型不适合有符号的void*,则使用unsigned int,例如在intshort具有相同大小的平台上。

答案 2 :(得分:0)

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

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