如何在整数指针数组中分配内存?

时间:2014-10-29 06:08:37

标签: c arrays pointers

我正在练习C编程并试图创建一个具有固定行但可变列的2D数组。所以,我使用了“指针数组”的概念,即我创建了一个数组,如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

所以,我认为内存已经这样分配: enter image description here 但是,在执行此代码时会产生问题:

#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]中寻找值,然后是[3] ] [i],数组b [3] [i]和b 1 [i]终止。

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

4 个答案:

答案 0 :(得分:2)

在评论中注明Hrishi,发生这种情况的原因是您尝试访问阵列末尾。那么实际发生了什么?

简短版本是您读取数组末尾并读入下一个数组(或未读取的内存)。但为什么会这样呢?

关于C风格数组的简要介绍

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

语法b[i]只是语法糖;它与*(b + i)相同,它正在进行指针运算。它字面上说:&#34;内存地址i位于b之后;告诉我指向那里&#34; 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也位于内存中的某个位置,但它可能位于此连续块之前或之后;我们不知道。

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

下一个显而易见的问题是:&#34; b[0][6]怎么办?为什么这么奇怪?&#34;

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

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

我应该注意到大多数其他语言(例如Java)不允许你做这样的事情;它会引发异常,因为你试图超出数组的范围进行访问。但是,C并不聪明。 C喜欢给你足够的绳索让自己挂起,所以你需要自己进行边界检查。


1 这是一种简化。 The truth is slightly more complicated
2 这个实现是数组索引从0开始的原因 3 这是undefined behaviour的一个例子,非常糟糕。基本上它意味着这个结果是不一致的。它现在每次都在你的计算机上以同样的方式发生。在朋友的计算机上,或者从现在起一小时内在计算机上试用它,你可能会得到完全不同的东西。
4 这是另一种过度简化,但为了您的目的,它足够接近真实

答案 1 :(得分:2)

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

您的b[2]e,这是一个元素的数组。有时您正在访问b[2][3]。这是buffer overflow(UB的一个实例)。真正发生的是特定于实现(可能因编译器,其版本,ABI,处理器,内核,月亮,编译器标志等而异)。您可能希望研究汇编代码以了解更多({{ 1}})。

顺便说一句,你不应该认为数组gcc -fverbose-asm -Scde有一些特定的内存布局。

答案 2 :(得分:2)

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

答案 3 :(得分:0)

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