1

我在 HSW 上遇到了这个程序:

int *p;
int i;

p = (int *)malloc(sizeof(int[10]));
for (i=0; i<10; i++)
    *(p+i) = 0;
free(p);

我不完全理解循环。假设内存是字节可寻址的,并且每个整数占用 4 个字节的内存,假设我们为p从 address 0to的指针分配 40 个字节的内存39
现在,据我了解,指针p最初包含 value 0,即第一个内存位置的地址。在循环中,将位移添加到指针以访问后续整数。

我无法理解内存地址 uptil39是如何以仅 的位移值访问的0 to 9。我检查并发现指针以4的倍数递增。这是怎么发生的?我猜这是因为整数类型指针,并且每个指针都应该增加它的数据类型的大小。这是真的?

但是,如果我真的想使用整数指针指向内存位置 2 怎么办。所以,我这样做:p = 2。然后,当我尝试取消引用这个指针时,我应该期待一个分段错误吗?

4

5 回答 5

2

现在,据我了解,指针 p 最初包含值 0

不,它包含 10 数组中第一个整数的地址。(假设 malloc 成功。)

在循环中,将位移添加到指针以访问后续整数。

嗯,没有。我不确定你的意思,但这不是代码的作用。

我检查并发现指针以4的倍数递增。这是怎么发生的?

指针算术,即在指针上使用 + - ++ -- 等运算符,足够聪明,可以知道类型。如果你有一个写 p++ 的 int 指针,那么存储在 p 中的地址将增加sizeof(int)字节数。

但是,如果我真的想使用整数指针指向内存位置 2 怎么办。所以,我这样做:p = 2。

不,不要那样做,这没有任何意义。它将指针设置为指向内存中的地址 0x00000002。


代码说明:

int *p;是一个指向整数的指针。通过编写*p = something,您可以更改p 指向的内容。通过写入p = something,您可以更改 p 指向的地址

p = (int *)malloc(sizeof(int[10]));是一个困惑的程序员写的。输入 malloc 的结果没有任何意义,您可以在此站点上找到有关该主题的大量信息。

写作sizeof(int[10])和写作一样10*sizeof(int)

*(p+i) = 0;和写作是一样的p[i] = 0;

我将按如下方式修复代码:

int *p = malloc(sizeof(int[10]));
if(p == NULL) { /* error handling */ }

for (int i=0; i<10; i++)
{
  p[i] = 0;
}

free(p);
于 2013-02-26T13:53:04.100 回答
2

由于您有一个类型化的指针,当您对其执行常见操作(加法或减法)时,它会自动调整您的类型的对齐方式。在这里,由于在您的计算机上sizeof (int)is 4p + i将导致 address p + sizeof (int) * i,或p + 4*i在您的情况下。

而且您似乎误解了该声明*(p+i) = 0。该语句等价于p[i] = 0。显然,您的malloc()电话不会返回 you 0,除非它未能实际分配您要求的内存。

然后,我假设您的最后一个问题的意思是“如果我将分配的地址恰好移动两个字节,会发生什么?”。

答案取决于您接下来要做什么以及系统的字节序。例如:

/*
 * Suppose our pointer p is well declared
 * And points towards a zeroed 40 bytes area.
 * (here, I assume sizeof (int) = 4)
 */

int *p1 = (int *)((char *)p + 2);
*p1 = 0x01020304;
printf("p[0] = %x, p[1] = %x.\n", p[0], p[1]);

将输出

p[0] = 102,p[1] = 3040000。

在大端系统上,并且

p[0] = 3040000, p[1] = 102

在小端系统上。

编辑:要回答您的评论,如果您尝试取消引用随机分配的指针,可能会发生以下情况:

  • 您很幸运:您键入的地址对应于已为您的程序分配的内存区域。因此,它是一个有效的虚拟地址。你不会得到一个段错误,但如果你修改它,它可能会破坏你的程序的行为(它肯定会......)
  • 你更幸运:地址无效,你得到了一个很好的段错误,可以防止你的程序完全搞砸。
于 2013-02-26T13:38:50.697 回答
2

现在,据我了解,指针 p 最初包含值 0

不,如果返回成功,指针p将不会保存该值。0malloc

在声明它的时候,指针是未初始化的,很可能是一个垃圾值。一旦将它分配给由 返回的malloc指针,指针就会指向分配器认为未占用的动态分配内存区域。

我无法理解直到 39 的内存地址是如何以只有 0 到 9 的位移值访问的

实际位移值为0, 4, 8, 12 ... 36。因为指针p有一个类型,在这种情况下int *,这表明在指针算术中应用的偏移量是sizeof(int),在你的情况下4。换句话说,位移乘数始终基于指针指向的类型的大小。

但是,如果我真的想使用整数指针指向内存位置 2 怎么办。所以,我这样做:p = 2。然后,当我尝试取消引用这个指针时,我应该期待一个分段错误吗?

确切的位置2很可能在您的进程的地址空间中不可用,因为该部分要么由操作系统保留,要么以另一种形式受到保护。所以从这个意义上说,是的,你会得到一个分段错误。

然而,在不能被其大小整除的位置访问数据类型的一般问题是打破了对齐要求。许多架构会坚持在 4 字节边界上访问整数,在这种情况下,您的代码将触发未对齐的内存访问,这在技术上是未定义的行为。

于 2013-02-26T13:40:48.840 回答
1

它被称为指针算术。将整数添加n到类型的指针会按元素t*移动指针。n * sizeof(t)因此,如果sizeof(int)是 4 个字节:

p + 1 (C) == p + 1 * sizeof(int) == p + 1 * 4 == p + 4

然后更容易索引您的数组:

*(p+i)是数组中的第 i 个整数p

于 2013-02-26T13:39:48.310 回答
0

我不知道“内存位置 2”是指您的示例内存地址2,还是指数组中的第二个值。如果您的意思是第二个值,那将是 memory address 1。要获得指向此位置的指针,您将执行int *ptr = &p[1];或等效操作int *ptr = p + 1;,然后您可以使用 打印此值printf("%d\n", *ptr);。如果您的意思是内存地址2(您的示例地址),那将是数组中的第三个值,那么您需要p[2]or p + 2。请注意,内存地址通常是十六进制的,实际上不会从 0 开始。它会类似于0x092ef000, 0x092ef004, 0x092ef008, . . .. 所有其他答案都不理解您正在使用内存地址0 . . . 39就像示例地址一样。老实说,我不认为您指的是从地址 0x00000000 开始的物理位置,如果您是,那么其他人所说的都是正确的。

于 2016-04-18T07:17:53.510 回答