了解 C 中数组元素中分配的内存地址(Windows 10 中的 gcc)

Understanding allocated memory addresses in array elements in C (gcc in Windows 10)

我正试图掌握 C 中的指针和数组。现在,我一直在努力弄清楚我的 C 编译器如何为二维数组中的元素分配内存。这是我的示例代码:

#include <stdio.h>

int main(void)
{
    int ar[2][2] = { {1, 2}, {3, 4} };

    printf("sizeof(int)      = %u\n-----\n", sizeof(int));

    printf("ar               = %p\n", ar);
    printf("ar + 1           = %p\n", ar + 1);
    printf("&ar              = %p\n", &ar);
    printf("&ar + 1          = %p\n\n", &ar + 1);

    printf("sizeof(ar)       = %u\n-----\n", sizeof(ar));

    printf("ar[0]            = %p\n", ar[0]);
    printf("ar[0] + 1        = %p\n", ar[0] + 1);
    printf("&ar[0]           = %p\n", &ar[0]);
    printf("&ar[0] + 1       = %p\n\n", &ar[0] + 1);

    printf("sizeof(ar[0])    = %u\n-----\n", sizeof(ar[0]));

    printf("ar[1]            = %p\n", ar[1]);
    printf("ar[1] + 1        = %p\n", ar[1] + 1);
    printf("&ar[1]           = %p\n", &ar[1]);
    printf("&ar[1] + 1       = %p\n\n", &ar[1] + 1);

    printf("sizeof(ar[1])    = %u\n-----\n", sizeof(ar[1]));

    printf("&ar[0][0]        = %p\n", &ar[0][0]);
    printf("&ar[0][0] + 1    = %p\n", &ar[0][0] + 1);
    printf("&ar[1][0]        = %p\n", &ar[1][0]);
    printf("&ar[1][0] + 1    = %p\n\n", &ar[1][0] + 1);

    printf("sizeof(ar[0][0]) = %u\n-----\n", sizeof(ar[0][0]));

    return 0;
}

我在系统上得到的输出是:

sizeof(int)      = 4
-----
ar               = 0061FF20
ar + 1           = 0061FF28
&ar              = 0061FF20
&ar + 1          = 0061FF30

sizeof(ar)       = 16
-----
ar[0]            = 0061FF20
ar[0] + 1        = 0061FF24
&ar[0]           = 0061FF20
&ar[0] + 1       = 0061FF28

sizeof(ar[0])    = 8
-----
ar[1]            = 0061FF28
ar[1] + 1        = 0061FF2C
&ar[1]           = 0061FF28
&ar[1] + 1       = 0061FF30

sizeof(ar[1])    = 8
-----
&ar[0][0]        = 0061FF20
&ar[0][0] + 1    = 0061FF24
&ar[1][0]        = 0061FF28
&ar[1][0] + 1    = 0061FF2C

sizeof(ar[0][0]) = 4
-----

我明白为什么ar是16个字节了;它应该能够容纳 4 ints,在我的系统上是 4x4 = 16 字节。我想这也是为什么 &ar + 1&ar 之间的字节差是(十六进制)30 - 20 = 16.

我不明白的是为什么ar + 1ar之间的差异只有8个字节。这意味着该数组只能容纳 2 ints á 4 个字节。

我在理解 ar[0]ar[1] 时遇到了同样的问题,正如您在我的代码中看到的那样。

ar + 1&ar + 1 不应该产生相同的结果吗?

在您的例子中,ar 是一个数组。因此,首先,请记住

  • arint [2][2]的类型,是ints
  • 数组的数组
  • &arint (*)[2][2] 类型,即指向 2 int 个数组的数组的指针。

也就是说,数组类型在某些情况下会衰减到指向数组第一个元素的指针。注意

所以,如果是

这样的表达式
ar + 1

相同
(&(ar[0])) + 1;

基本上指向 ar[1]

What I don't understand is why the difference between ar + 1 and ar is only 8 bytes

所以,这里的"difference",就是被ar[0]的元素占用的大小,也就是2ints,在您的平台中,这是 8 个字节。结果签出。

另一方面,对于像

这样的表达式
&ar + 1;

它对指针类型进行操作(如前所述),并指向数组中最后一个元素之后的位置。因此, 差异 是,对于每个 2 个 ints 的 2 个数组,因此 (2*2*4) = 16 个字节。


注:

引用 C11,章节 §6.3.2.1

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. [....]

ar 在表达式中使用时,"decays" 指向第一个元素的指针。在这种情况下,arr + 1 给出了 int (*)[2] 类型指针的算术运算。它指向大小为 8 字节的 int [2]

"array decay" 的这条规则在 C17 6.3.2.1 §3 中指定:

Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue

因此,当您键入 &ar 时,您会得到数组衰减规则的特殊例外,不会发生衰减,但您实际上会得到预期的 int (*)[2][2]。因此 &ar + 1 给出 16 个字节。

所以:

sizeof(int) == 4

以下:

int ar[2][2];

是一个二维数组。

我们知道,a[b]等于*(a + b)。并且&*被转换为空。

所以:

&ar[1]

等于

(ar + 1)

这里 ar "decays" 或 "shall be adjusted" (读作:神奇地转换)为指针。指向两个 int 元素数组的指针,即。 int (*)[2]。所以它不是 int * 也不是 int[2][2] 指针,而是 int (*)[2]。我们知道

sizeof(ar) == sizeof(int[2][2]) == sizeof(int[2]) * 2 == sizeof(int) * 2 * 2
sizeof(*ar) == sizeof(*(int(*)[2]) == sizeof(int[2]) == sizeof(int) * 2
sizeof(**ar) == sizeof(**(*(int(*)[2])) == sizeof(*(int[2])) == sizeof(*(int*)) == sizeof(int)

所以

(ar + 1)

等于(到值):

(uintptr_t)ar + sizeof(*ar) * 1 == 
    (uintptr_t)ar + sizeof(*(int(*)[2])) * 1) ==
    (uintptr_t)ar + sizeof(int[2]) * 1) == 
    (uintptr_t)ar + sizeof(int) * 2 * 1)

即。它将 ar 指针值增加 2 * sizeof(int)

What I don't understand is why the difference between ar + 1 and ar is only 8 bytes.

ar + 1 等于

(uintptr_t)ar + sizeof(*ar) + 1

因为arint[2][2],所以*arint[2],所以sizeof(*ar) = sizeof(int) * 2.
所以 ar + 1 等于

(uintptr_t)ar + sizeof(int) * 2 * 1

所以 (ar + 1) - ar 等于

((uintptr_t)ar + sizeof(int[2]) * 1) - (uintrpt_t)ar ==
    sizeof(int[2]) == 
    sizeof(int) * 2

Shouldn't ar + 1 and &ar + 1 produce the same result?

对于像 int array[2]; 这样的数组,array 指针值等于 &array 指针值。这是 C 的一个怪癖,将寻址运算符应用于数组会导致数组指针指向同一内存。通过 array 具有 int[2][2] 类型,但 &array 具有 int(*)[2][2] 类型,即。它是一个指向二维数组的指针。

因为类型改变了,指针算法也改变了。 Teh typeof(ar) 衰减到 typeof(int(*)[2]) 所以 ar + 1 等于

`(uintptr_t)ar + sizeof(int[2]) * 1`. 

但是因为 typeof(&ar) == typeof(int(*)[2][2]) &ar + 1 等于

`(uintrpt_t)ar + sizeof(int[2][2]) * 1`.

因此指针递增时指针值的差异,因为 sizeof(int[2][2]) 等于 sizeof(int) * 2 * 2

我认为您没有理解,在二维数组的情况下,"first" 级别是两个元素的一维数组,而不是第二个是 int。所以 typeof(ar[0]) 是一个包含两个 int 元素的数组。

您的代码有 UB,因为 %p 修饰符只能与 void* 指针一起使用。最好记住(或者至少知道你应该)printf("%p", (void*)&ar[1][0] + 1); 投出你的指点。