向空指针地址添加整数,返回意外结果

问题描述

#include <stdio.h>

int main() {
  int *numPtrA = NULL;         // pointer to an integer pointer to null.. ascii value of zero
 
  printf("%p\n",numPtrA + 2); // prints 0x8 which i expect
  printf("%p\n",numPtrA + 5); // prints 0x14 and i dont kNow why
}

enter image description here

我试图通过向空指针的ASCII码添加一些东西来打印地址0x14,我认为这是0地址。但是,当我将5加到NULL时,我不明白为什么我得到0x14而不是0x20。我以为ptrNumA是一个指向整数的指针,因此应该添加5个整数块(每个4字节),新地址应为0x20

感谢您的帮助。非常感谢。

解决方法

从技术上讲,numPtrA既不指向数组也不指向单个对象,因此,向其添加除0以外的任何值都会导致C ++中出现未定义的行为,因为指针算术仅针对数组范围内的指针定义和物体。

这是最新的C ++标准草案的引文:

[expr.add]

将具有整数类型的表达式J添加到指针类型的表达式P或从其中减去时,结果的类型为P。

  • 如果P的值为空指针值,J的值为0,则结果为空指针值。 [不适用]
  • 否则,如果P指向具有n个元素的数组对象x的数组元素i ... [不适用] (注意:指向对象的指针可以视为数组此规则中1个元素的数量)
  • 否则,行为是不确定的

除技术性外,前缀0x表示十六进制基数。 5 * 4 = 20 = 0x140x20 = 32 != 20

也就是说,空指针的值不一定是0(即使文字0始终是空指针文字),因此期望值不能移植到某些不是这种情况的系统中。这在嵌入式系统中很常见,在该系统中实际存储需要使用地址0。

此外,当您传递printf作为参数时,%p说明符void*要求参数的类型为int*。违反此约束的结果是程序的行为未定义。

,

答案在评论中。 在您的情况下,sizeof(int) == 4并因此numPtr + 2指向地址8 = 2*4,而numPtr + 5指向地址20 = 5*4,它实际上是十六进制表示的0x14

,

不允许您对未指向有效对象的指针进行指针算术-这是未定义的行为(根据6.5.6,详细信息和引号here)。这样您就可以得到任何结果。

此外,不保证在C中空指针的二进制表示形式为零。我建议学习What's the difference between null pointers and NULL?

然后最后,%p以十六进制给出输出。指针算术的基础是typeptr + 5等于以字节表示的加sizeof(*typeptr) * 5。由于sizeof(int)可能为0x4,因此您得到0x4 * 0x5 = 0x14