【问题标题】:How is memory allocated in an array of integer pointers?如何在整数指针数组中分配内存?
【发布时间】:2014-10-29 06:08:37
【问题描述】:

我正在练习 C 编程并尝试创建一个具有固定行但可变列的二维数组。所以,我使用了“指针数组”的概念,即我创建了一个数组,例如 int* b[4]。

这是编写的代码:

#include <stdio.h>

int main(void) {

int* b[4];

int c[]={1,2,3};
int d[]={4,5,6,7,8, 9};
int e[]={10};
int f[]={11, 12, 13};

b[0]=c;
b[1]=d;
b[2]=e;
b[3]=f;

//printing b[0][0] to b[0][2] i.e. c[0] to c[2]
printf("b[0][0]= %d\tb[0][1]=%d\tb[0][2]=%d\n", b[0][0], b[0][1], b[0][2]); 

//printing b[1][0] to b[1][5] i.e. d[0] to d[5]
printf("b[1][0]= %d\tb[1][1]=%d\tb[1][2]=%d\tb[1][3]=%d\tb[1][4]=%d\tb[1][5]=%d\n", b[1][0], b[1][1], b[1][2], b[1][3], b[1][4], b[1][5]);

//printing b[2][0] i.e. e[0]
printf("b[2][0]= %d\n", b[2][0]); 

//printing b[3][0] to b[3][2] i.e. f[0] to f[2]
printf("b[3][0]= %d\tb[3][1]=%d\tb[3][2]=%d\n", b[3][0], b[3][1], b[3][2]); 

return 0;

}

输出如预期:

b[0][0]= 1  b[0][1]=2   b[0][2]=3
b[1][0]= 4  b[1][1]=5   b[1][2]=6   b[1][3]=7   b[1][4]=8   b[1][5]=9
b[2][0]= 10
b[3][0]= 11 b[3][1]=12  b[3][2]=13

所以,我认为内存是这样分配的: 但是,执行此代码时会出现问题:

#include <stdio.h>

int main(void) {

int* b[4];

int c[]={1,2,3};
int d[]={4,5,6,7,8, 9};
int e[]={10};
int f[]={11, 12, 13};

b[0]=c;
b[1]=d;
b[2]=e;
b[3]=f;

int i, j;
for (i=0; i<4; i++)
{
    for (j=0; j<7; j++)
    {
        printf("b[%d][%d]= %d ", i, j, b[i][j]);
    }
    printf("\n");
}



return 0;

}

而且输出有些不寻常:

 b[0][0]= 1 b[0][1]= 2 b[0][2]= 3 b[0][3]= 11 b[0][4]= 12 b[0][5]= 13 b[0][6]= -1079668976 
b[1][0]= 4 b[1][1]= 5 b[1][2]= 6 b[1][3]= 7 b[1][4]= 8 b[1][5]= 9 b[1][6]= -1216782128 
b[2][0]= 10 b[2][1]= 1 b[2][2]= 2 b[2][3]= 3 b[2][4]= 11 b[2][5]= 12 b[2][6]= 13 
b[3][0]= 11 b[3][1]= 12 b[3][2]= 13 b[3][3]= -1079668976 b[3][4]= -1079668936 b[3][5]= -1079668980 b[3][6]= -1079668964 

可以观察到 b[0][i] 继续从 b[3][i] 中寻找值,数组 b[2][i] 继续从 b[0][i] 后跟 a[3 寻找值][i],数组 b[3][i] 和 b1[i] 终止。

每次执行此程序时,都会遵循相同的模式。那么,在分配内存的方式上是否还有更多内容,或者这仅仅是巧合?

【问题讨论】:

  • 您正在访问内存中超出数组范围的位置,这些位置中存储的任何内容,换句话说就是垃圾,正在被打印出来。
  • 当您分配b[0]=c; 时,您认为b[0] 中存储的是什么?如果您尝试迭代 j &gt; 2 会发生什么?您必须有某种方法不尝试读取超出为c, d, e &amp; f 分配的空间的末端。由于它们的大小不同,您要么必须将迭代具体限制为每个适当的size,要么有其他方式来表示数据的结束。
  • 编译所有警告和调试信息 (gcc -Wall -Wextra -g)。然后使用调试器 (gdb)。当心undefined behavior

标签: c arrays pointers


【解决方案1】:

正如 cmets 中的 Hrishi 所述,发生这种情况的原因是您试图访问超出数组末尾的内容。那么到底发生了什么?

简短的版本是您正在读取数组的末尾,然后读取到下一个数组(或未分配的内存)。但是为什么会这样呢?

关于 C 样式数组的简要介绍

在 C 中,数组只是指针1b 是指向数组开头的指针,因此*b 将返回数组的第一个元素(在本例中是指向b[0] 开头的指针。

语法b[i] 只是语法糖;和*(b + i)一样,都是做指针运算的。它的字面意思是:“内存地址i 位于b 之后;告诉我那里指的是什么”2

因此,例如,如果我们查看b[0][3],我们可以将其转换为*((*b) + 3):您将获取b 的开头地址,然后获取存储在三个内存地址之外的任何内容那个。

那么你怎么了?

碰巧的是,您的计算机已从该地址开始存储b[3]。这就是它真正告诉您的内容:您的计算机将每个子阵列放置在内存中的位置。这是因为数组总是连续排列的,在内存中一个接一个的位置(这就是指针算术技巧的工作原理)。但是因为您分别定义了cdef,内存管理器没有将它们分配到彼此相邻的位置,而是将它们放在任何它想要的位置。由此产生的模式正是它想出的。据我所知,您的数组在内存中的布局如下:

--------
| e[0] |
--------
| c[0] |
--------
| c[1] |
--------
| c[2] |
--------
| f[0] |
--------
| f[1] |
--------
| f[2] |
--------

d 也位于内存中的某个位置,但它可能位于此连续块之前或之后;我们不知道。

但是你不能依赖这个。正如我在脚注中提到的,分配内存的顺序不是由语言定义的,因此它可以(并且确实)根据任意数量的因素而改变。明天运行同样的代码,它可能不会完全一样。

下一个明显的问题是:“b[0][6] 呢?为什么这是一个如此奇怪的数字?”

答案是你的数组已经用完了,你现在正试图从未分配的内存中读取。

当您的程序开始运行时,操作系统会为其分配一定的内存块并说“在这里,随心所欲地使用它”。当您在堆栈(如您在这里)或堆(使用malloc)上声明一个局部变量时,内存管理器会抓取一些内存并将其返回给您4。您当前未使用的所有内存仍然存在,但您不知道那里存储了什么;它只是最后一次使用该特定内存块的剩余数据。在 C 中读取这也是 undefined behaviour,因为您显然无法控制该内存中存储的内容。

我应该注意到大多数其他语言(例如 Java)不允许你做这样的事情;它会抛出异常,因为您试图访问超出数组的范围。然而,C 并不那么聪明。 C 喜欢给你足够的绳子让你上吊,所以你需要自己做边界检查。


1 这是一个简化。 The truth is slightly more complicated
2 这个实现就是数组索引从 0 开始的原因。
3 这是一个 undefined behaviour 的例子,非常糟糕。基本上这意味着这个结果是不一致的。现在,在您的计算机上,每次都以相同的方式发生。在朋友的电脑上试一试,甚至一小时后在你的电脑上试一试,你可能会得到完全不同的结果。
4 这是另一种过于简单化的方法,但对于你的目的来说,它已经足够接近真实了

【讨论】:

    【解决方案2】:

    你的小图是对的,唯一的事情是,由于你在函数中顺序声明了数组,它们都在堆栈中,并排。因此,通过访问超出数组的限制,您将访问下一个数组。

    【讨论】:

      【解决方案3】:

      编译所有警告和调试信息 (gcc -Wall -Wextra -g)。然后使用调试器 (gdb)。 小心 undefined behavior (UB)。

      您的b[2]e,它是一个一个 元素的数组。有时您正在访问b[2][3]。这是buffer overflow(UB 的一个实例)。真正发生的是实现特定的(可能会因编译器、其版本、ABI、处理器、内核、moon、编译器标志而异,...)您可能想研究汇编代码以了解更多信息(@987654328 @)。

      顺便说一句,您不应该假设数组 cdef 具有某些特定的内存布局。

      【讨论】:

        【解决方案4】:

        当您使用特定位置地址打印数组元素的值时,您将获得准确的数组值。但是当您执行相同的程序时,您会得到garbage values,就像在c 语言中我们在C 中没有bond checking。因此,当您尝试访问超出您使用的内存范围的位置的值时,您得到的只是存储在该内存上的数据,也称为garbage value。因此,要获得正确的结果,您需要检查访问数组键中的值或为该数组定义的限制中的值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-29
          • 2015-12-10
          • 2019-06-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-15
          • 1970-01-01
          相关资源
          最近更新 更多