我写了
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
我希望每个地址都能看到一个字节。但是,我看不到这样的东西。为什么?
答案 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
所读取的内容不兼容,则程序的行为是不确定的。
您碰巧通过了int
。 int
不是unsigned int
,但是它足够接近,以至于当您读取期望int
的{{1}}时,如果整数为正或零,则该值保持不变。负整数映射到一个较大的正值(在使用二进制补码表示的机器上,unsigned int
几乎是所有机器)。
如果在期望UINT_MAX - a
(是否签名)的情况下将char
(是否签名)传递给printf
,则该行为的定义很明确,这是因为C语言,这是促销活动。小于int
(即int
和char
)的整数类型的值将转换为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
,例如在int
和short
具有相同大小的平台上。
答案 2 :(得分:0)
如果要打印字节,请按字节播放:
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)。