我是计算机科学的初学者。我了解到,指针是一种复合数据类型,它指示数据的地址以及数据的类型和大小。指针的类型转换只会更改读取大小,但会更改起始地址。为了证实这一点,我做了一个实验。
请参见下面的代码。我更改了变量“样本”的指针类型,但我认为它仍指向样本的第一个字节。大小没有改变。然后,我使(字符类型)指针向左跳一个字节(在代码中为“ p = p-1”)。之后,我将其转换回短类型。我认为指向的数据是0x..24(..表示数据在0x2456前面)。最后,我使用位操作“ <<”更改为0x2400。但是,每次运行时,我都会得到随机数。
#include<stdio.h>
int main(void){
short sample = 0x2456;
char *p = (char*) &sample;
p = p-1;
printf("%d\n",*((short*)p)<<8 );
return 0;
}
答案 0 :(得分:1)
出于网络安全原因,大多数operating systems都提供address space layout randomization,其中包括call stack调用的main
函数的crt0。
我不知道为什么无法获得固定值。
ASLR可能解释了为什么在OS上多次运行程序会产生不同的输出。您的p
可能指向调用堆栈的某个奇怪位置。
当然,请阅读有关undefined behavior的更多信息,以及Modern C以及C11的规范,即n1570。
如果您使用最新的GCC来编译C代码foo.c
,请考虑使用gcc -Wall -Wextra -O -S foo.c
对其进行编译,然后查看发出的汇编代码foo.s
。然后,您将了解将什么值传递给printf
。它是特定于实现的。
答案 1 :(得分:1)
存在C语言,其语言可以保证,然后有未定义的代码,不能保证任何特定行为。
C语言不允许允许所有形式的通配符转换和任意指针算术-如果违反规则,则可能发生任何事情,并且不能保证确定性的结果。如果要分析此类不确定性代码,则可能会得到任何结果,包括程序崩溃。
要详细分析代码并显示错误之处:
char *p = (char*) &sample;
这是有效的转换。 C中有一些特殊的规则,允许通过诸如此类的指针转换将任何类型转换为字符类型。进行这种转换后,您可以取消引用指针。char
特别具有实现定义的签名。这意味着它可以等于signed char
或unsigned char
,具体取决于编译器。如果已签名,那么如果取消引用该指针,则可能会遇到意外的输出。 p = p-1;
是未定义的行为,任何事情都可能发生。我们只能对指向数组的指针进行指针算术运算。为了确定指针算术是否有效,将指向单个值变量(“标量”)的指针视为指向具有1个项的数组的指针。
指针算术必须导致指针指向已分配数组中从索引0
到n+1
的数组项,其中n
是数组大小。 C允许将1项指向数组之外的一种特殊情况。但是,它不允许指针算术像在代码中那样导致指针指向数组之前的项。
(作为一种特殊情况,为了分析原始数据,我们可以使用字符指针对任何数据类型进行增量迭代。然后将数据视为sizeof(data)
个字符的数组。但是您的代码无法执行此操作。)
*(short*)p
也是未定义的行为,因为编译器不知道p
现在指向何处或在那里存储什么类型。在所有需要 aligned access 的系统上,这也是未定义的行为,因为指针现在绝对是未对齐的。
最后,您现在访问的内存区域可能会被系统阻止,从而导致访问冲突或分段错误。这种错误的性质和结果超出了C的范围。
尽管上述所有问题,如果您的程序仍设法吐出一些二进制结果,则该结果可能会受到CPU endianess 的影响。在第一行char *p = (char*) &sample;
,p
可以指向MS字节或LS字节,具体取决于系统。
如果前一句话中未定义的二进制粘滞符恰好包含MSB集,则可以通过左移负整数再次调用未定义的行为。
摘要:我计算了4种未定义行为的情况和2种实现定义行为的情况。此代码没有预期的保证或确定性结果。检查结果没有什么可学的。您只能通过分析给出此类结果的代码为什么错误来学习一些东西。
答案 2 :(得分:0)
根据您的代码,我假设您想将0x24
移到较高(实际上是较低)的字节上。尝试p=p+1
,看看是否获得理想的结果。
如果您执行(int)*p << 8
,则每次读取2400都比读取指针短。
或者您可以做一些疯狂的事情,例如在初始化指针之后初始化一些变量,这样当我们移动指针时,它不会得到垃圾,而是变量的一部分
#include<stdio.h>
int main(void){
short sample = 0x2456;
char *p = (char*) &sample;
int zero = 0;
p = p+1;
printf("%x\n",*((short*)p) << 8 );
return 0;
}
您甚至可以像这样打印0x1224(两者的一半)
#include<stdio.h>
int main(void){
short sample = 0x2456;
char *p = (char*) &sample;
int zero = 0x12;
p = p+1;
printf("%x\n",*((short*)p); //will print 1224
return 0;
}
边注:我假设两个变量都在单个堆栈帧中,并且假定内存为低端字节序。结果可能会因编译器和目标系统而异。
答案 3 :(得分:-1)
我只是要在黑暗中拍照。如果要打印存储在样本的前8位中的值(在这种情况下为1)。注意,这假设内存是小端的:
short sample = 1;
char* p = (char*)&sample;
p = p - 1;
std::cout << "the first 8 bits of sample: " << (*(short*)p >> 8) << std::endl;