内存分配没有使用malloc分配,怎么样?

以下是一个简单的代码段:

int main() { int *p; p=(int*)malloc(sizeof(int));//allocate m/y 4 1 int printf("P=%p\tQ=%p",p,p+2); } 

在一个示例运行中,它给出了如下输出:

 P=0x8210008 Q=0x8210010 

P的起始地址是-P = 0x8210008,下一个字节是0x8210009,下一个字节是0x821000A,下一个字节是0x821000B。因此int的4个字节在那里结束。 我们没有使用malloc分配更多的内存。 那么p + 2如何将我们引向0x8210010,这是P(0x8210008)之后的8个字节。

首先,您打印了一个地址的事实并不意味着在该地址分配了内存。 您只需添加数字并生成其他数字。

第二,你加上两个你得到的数字的原因是比基地址大8,而不是比基地址大两个因为,当你在C中的指针中添加整数时,算术是根据指向的元素完成的,而不是内存中的字节数(除非指向的元素是字节)。 假设你有一个int数组,比如int x[8] ,你有一个指向x[3]的指针。 向该指针添加两个指针会产生一个指向x[5]的指针,而不是指向超出x[3]开头的两个字节的指针。 重要的是要记住C是一个抽象,C标准指定了抽象内部发生的事情。 在C抽象中,指针算法对元素的数量起作用,而不是在原始内存地址上。 C实现(将C代码转换为程序执行的编译器和工具)需要执行对原始内存地址执行的任何操作,以实现C标准指定的抽象。 通常,这意味着编译器在将整数添加到指针时将整数乘以元素的大小。 因此,两个乘以4(在一个int为四个字节的机器上),并将结果的八个加到基址上。

第三,你不能依赖这种行为。 C标准仅为指向数组内对象的指针定义指针算法,包括数组末尾的一个虚构对象。 另外,指向单个对象的指针就像一个元素的数组。 因此,如果你有一个指向int的指针p ,你可以计算p+0p+1 ,因为它们指向数组中唯一的对象( p+0 )和超出最后一个的虚拟对象数组中的元素( p+1 )。 您不能计算p-1p+2 ,因为它们在数组之外。 请注意,这不是取消引用指针(尝试在计算的地址读取或写入内存)的问题:即使仅计算地址也会导致C标准未定义的行为:您的程序可能会崩溃,它可能会给您“正确”的结果,或者它可能会删除您帐户中的所有文件,所有这些行为都符合C标准。

仅仅计算越界地址不太可能产生这种奇怪的行为。 但是,该标准允许它,因为一些计算机处理器具有不寻常的地址方案,需要比简单算术更多的工作。 也许平坦地址空间之后的第二常见地址方案是基地址和偏移方案。 在这种方案中,四字节指针的高16位可能包含基址,而低16位可能包含偏移量。 对于给定的基址b和偏移量o,相应的虚拟地址可能是4096 * b + o。 (这种方案只能处理2 20个字节,并且许多不同的base和offset值可以指相同的地址。例如,base 0和offset 4096指的是与base 1相同的地址和offset 0.)基本和偏移方案,编译器可以通过仅添加偏移量并忽略基数来实现指针算法。 (这样的C实现只能支持最多65536个字节的数组,这个范围只能由偏移量来解决。)在这样的实现中,如果你的指针指向int p ,编码为0x0000fffc(base 0,offset 65532),和int是四个字节,然后p+2将具有值0x00000004,而不是八个更大的值(0x00010004)。

这是一个示例,其中指针算术产生您不希望从平面地址机器中获得的值。 很难想象一个实现,其中根据C标准无效的指针算法会产生崩溃。 但是,请考虑一种实现,其中内存必须由进程手动交换,因为处理器没有硬件来支持虚拟内存。 在这样的实现中,指针可能包含内存中描述磁盘位置的结构的地址以及用于管理内存交换的其他信息。 在这样的实现中,执行指针运算可能需要读取存储器中的结构,因此无效指针算法可能会读取无效地址。

因为它将它视为指针的整数元素偏移量。 您已为单个整数分配了一个数组。 当你要求p+2它与&p[2] 。 如果您想从头开始使用两个字节,则需要先将其char*char*

 char *highWordAddr = (char*)p + 2; 

C很高兴让你做任何你喜欢的指针算法。 仅仅因为p+2看起来像任何其他地址并不意味着它是有效的。 事实上,在这种情况下,它不是。

每次看到指针运算时都要非常小心,不要超出分配范围。