【问题标题】:Memory address for arrays allocated on heap and on stack [duplicate]在堆和堆栈上分配的数组的内存地址[重复]
【发布时间】:2021-05-10 05:29:04
【问题描述】:

我刚刚完成了大学的С语言课程。有人告诉我变量是如何存储在 pc 内存中的,并且数组 是连续存储的。 在以下示例中可以看到:

#include <stdio.h>
void main(){
 char a[3][6];
 printf("[0][0]->%p \t[1][0]->%p\t [0][5] %p \n", &a[0][0],&a[1][0],  &a[0][5]);
 printf("%p %p", a, &a[0][0]);
}

输出会是这样的:

[0][0]->0061FF06        [1][0]->0061FF0C         [0][5] 0061FF0B
0061FF06 0061FF06

并且! a地址将与a[0][0] 的地址相同。 并且 sizeof(a) 是 18(在这个例子中)。 但是......说到动态数组......这里,你自己看看。

#include <stdio.h>
#include <stdlib.h>
void main()
{
    char **a = (char **)malloc(3 * sizeof(char*));
    for(int i=0; i < 3; i++)
        a[i] = (char *)malloc(6 * sizeof(char));

    printf("[0][0]:%p [1][0]%p [0][5]%p\n", &a[0][0], &a[1][0], &a[0][5]);
    printf("a:%p [0][0]: %p\n", a, &a[0][0]);
    printf("[1][0] - [0][5] = %d \n", &a[1][0] - &a[0][5]);
    printf("%d", sizeof(a) );
}

结果会是这样的:

[0][0]:00AD1588 [1][0]00AD1598 [0][5]00AD158D
a: 00AD15D8 [0][0]: 00AD1588
[1][0] - [0][5] = 11
4
  1. 为什么在动态 [1][0] - [0][5] 中不等于 1(如在静态数组中)?
  2. 为什么在动态 sizeof(a) 中是 4 ?
  3. 为什么a&amp;a[0][0] 的动态地址中不妥协?

【问题讨论】:

  • 因为你所说的dynamic array 实际上是一个指针,它的行为非常不同。
  • "数组是连续存储的" - 这不是真的 - 我认为你的意思是数组 元素 是顺序存储的。
  • 为什么,因为二维动态数组不同于二维静态数组,(前者不是连续存储来给出一个简单的区别)为什么你认为它们应该是一样吗?
  • @john 是时候给班级教授在班级评估表上打低分了,我想。
  • 您的两个示例等效。当您拥有a[3][6] 时,所有元素在内存中都是连续的。当你这样做时:int **a = malloc(...);然后循环填充malloca中的指针数组是连续的,但a的每个元素都是单独分配的,不会是连续的。如果您想要一个连续的动态数组,请执行以下操作:int (*a)[3][6] = malloc(sizeof(int) * 3 * 6);

标签: arrays c memory dynamic


【解决方案1】:

您声明了一个名为 a 的指针

char **a = (char **)malloc(3 * sizeof(char*));

在您的系统中,sizeof( char ** )sizeof( a ) 相同,等于 4。

即指针的大小与动态分配数组的大小没有任何共同之处。它只存储数组分配范围的地址。

至于其他问题,您没有分配二维数组。您分别分配了四个数组。一个数组包含 char * 类型的元素,并且数组的每个元素都指向一个单独分配的数组,其中包含 char 类型的元素。

例如,你的问题的答案

为什么在 a 和 &a[0][0] 的动态地址中不一致

如下。

变量a 有一个地址。它与元素类型为char * 的动态分配的一维数组没有什么共同之处,只是变量a 存储了分配数组的地址。

顺便说一句,这与动态内存分配没有共同之处。

考虑以下演示程序。

#include <stdio.h>

int main(void) 
{
    char a[3][6];
    char ( *p )[6] = a;
    
    printf( "a = %p\n", ( void * )a );
    printf( "p = %p\n", ( void * )p );
    printf( "&p = %p\n", ( void * )&p );

    return 0;
}

它的输出可能看起来像

a = 0x7ffc08157b50
p = 0x7ffc08157b50
&p = 0x7ffc08157b48

如您所见,变量p (&amp;p) 的地址与数组占用的范围地址不同,因为变量p 是一个单独的变量,它占用自己的内存范围。

对于数组,表达式中使用的数组指示符被隐式转换为指向它们的第一个元素的指针。

如果你确实会分配一个二维数组,你可以获得预期的结果(除了与 sizeof 运算符相关的结果)

char ( *a )[6] = malloc( sizeof( char[3][6] ) );

【讨论】:

    【解决方案2】:

    在您的第一个示例中,a 是一个包含 3 个元素的一维数组,其中每个元素都是一个 char[6] 数组。由于给定数组的元素在内存中是连续的,因此整个数组是连续的:

    -------------------------------------------------------------------------------------
    | ------------------------- | ------------------------- | ------------------------- |
    | | 0 | 1 | 2 | 3 | 4 | 5 | | | 0 | 1 | 2 | 3 | 4 | 5 | | | 0 | 1 | 2 | 3 | 4 | 5 | |
    | ------------------------- | ------------------------- | ------------------------- |
    -------------------------------------------------------------------------------------
                 a[0]                        a[1]                        a[2]
    

    但是,在您的第二个示例中,a一个指针,指向具有 3 个元素的一维数组,其中每个元素都是 一个指针,指向一个单独的 @ 987654325@ 数组位于内存中的其他位置,malloc() 决定分配它们的位置:

                                     -------------------------
                                     | 0 | 1 | 2 | 3 | 4 | 5 |
    -------------------------        -------------------------
    | 0 | 1 | 2 | 3 | 4 | 5 |        ^
    -------------------------        a[1]
    ^                                |
    a[0]              ----------------
    |                 |
    |                 |     -------------------------
    |                 |     | 0 | 1 | 2 | 3 | 4 | 5 |
    |                 |     -------------------------
    |                 |     ^
    |__________       |     a[2]
               |      |     |
             -------------------
             |  0  |  1  |  2  |
             -------------------
             ^
             a
    

    【讨论】:

    • 哎呀,你做记忆图的速度比我快得多!投了赞成票!
    【解决方案3】:

    如果您了解数组与指向数组的指针在内存中的表示方式,这会有所帮助。

    // This can be on the heap or on the stack, the representation is the same:
    char a[3][6];
    

    如果a 位于 0x1000,则如下所示:

    • char[6] 的每个子数组都分组在方括号中:[00 00 00 00 00 00],并且所有子数组都具有相同的值,如下所示:
      • a[0][0..n] = 0x00
      • a[1][0..n] = 0x11
      • a[2][0..n] = 0x22
               0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    ------------------------------------------------------------------------
    0x1000   [00  00  00  00  00  00][11  11  11  11  11  11][22  22  22  22     
    0x1010    22  22] 00  00  00  00  00  00  00  00  00  00  00  00  00  00
    0x1020    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    

    您可以看到从0x10000x1011 的18 个连续元素。


    现在这个:

    char **a = (char **)malloc(3 * sizeof(char*));
    for(int i=0; i < 3; i++) {
        a[i] = (char *)malloc(6 * sizeof(char));
    }
    

    ...看起来像这样:

    • 假设是 32 位 big-endian 指针(为清楚起见 big-endian,实际上 x86 是 little-endian)。
    • a 存在于 0x1000
      • a[0] 指向 0x1030
      • a[1] 指向 0x1040
      • a[2] 指向 0x1050
    • 每个子数组元素都具有相同的值,如上,并用方括号表示:
      • a[0][0..n] = 0x00
      • a[1][0..n] = 0x11
      • a[2][0..n] = 0x22
               0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    ------------------------------------------------------------------------
    0x1000   [00  00  10  30][00  00  10  40][00  00  10  50] 00  00  00  00 
    0x1010    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    0x1020    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
    0x1030   [00  00  00  00  00  00] 00  00  00  00  00  00  00  00  00  00
    0x1040   [11  11  11  11  11  11] 00  00  00  00  00  00  00  00  00  00
    0x1050   [22  22  22  22  22  22] 00  00  00  00  00  00  00  00  00  00
    

    现在您看到a(本身)的分配大小为 12 个字节,整个 2D 数组结构在内存中不再连续 - 这就是为什么您的指针减法结果没有意义。

    【讨论】:

      猜你喜欢
      • 2012-11-02
      • 2018-09-17
      • 2017-11-08
      • 2011-10-09
      • 1970-01-01
      • 2018-02-04
      • 2017-01-04
      • 2012-12-05
      • 1970-01-01
      相关资源
      最近更新 更多