2012-07-31 90 views
-1

下面是一个简单的代码片断:使用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个字节。

+3

在C中,你永远不会通过像这样的测试获得很远的学习;由于*未定义的行为*你会发现很多不一致 - 你都不应该指望它们。 – Dave 2012-07-31 05:05:51

+1

请首先使用SO搜索工具:[我可以使用更多的内存,而不是使用malloc()分配多少内存,为什么?](http://stackoverflow.com/questions/3509714/i-can-使用多内存而不是使用多少分配与malloc-why) – 2012-07-31 09:56:53

回答

4

首先,事实证明你已经打印的地址并不意味着在该地址被分配内存。您只需添加数字并生成其他数字。

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

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

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

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

1
+0

不,它被称为未定义的行为。 – 2012-07-31 04:00:52

+0

@R它不是未定义的,如果P = 0x8210008的地址比p + 2的地址必然是0x8210010 – AlexDev 2012-07-31 04:02:39

+3

否则不会。指针运算仅在数组内定义,并且直到位于最后一个数组元素的“过去”位置。由于所讨论数组的长度为1,所以加2会超出这些界限,从而调用未定义的行为。 – 2012-07-31 04:06:05

4

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

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

+0

这不能回答提问者的问题,因为它不能解释为什么他们得到的值比基地址大8而不是2大。首先,如果他们打印了p + 1,他们将得到一个更大的地址4,因为向指针添加一个整数产生该类型的下一个对象的地址,而不是内存中下一个字节的地址,所以C实现实现根据需要进行地址算术以实现此目的。其次,如其他注释中所述,加2会超出编译器所需的指针算术范围,所以行为不确定。 – 2012-07-31 08:01:47

5

因为它将它视为指针的整数元素偏移量。您已经为单个整数分配了一个数组。当您要求p+2时,它与&p[2]相同。如果你想从一开始两个字节,你需要将其转换为char*第一:

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