【问题标题】:Understanding allocated memory addresses in array elements in C (gcc in Windows 10)了解 C 中数组元素中分配的内存地址(Windows 10 中的 gcc)
【发布时间】:2018-11-26 10:30:04
【问题描述】:

我试图掌握 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 个字节。我猜这也是为什么&amp;ar + 1&amp;ar 之间的字节差是(十六进制)30 - 20 = 16。

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

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

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

【问题讨论】:

  • 您需要使用 () 来设置优先顺序,[] 在 () 之后的优先级最高,您的表达式不会像现在写的那样是您所期望的。例如。 *ar[0] 与 (*ar)[0] 不同
  • 好点,谢谢。但是,在我的代码中,我想要 ar[0] 的地址,用 &ar[0] 还不够,还是应该使用 &(ar[0])?

标签: c arrays memory pointer-arithmetic


【解决方案1】:

在您的情况下,ar 是一个数组。因此,首先,记住

  • arint [2][2]的类型,是ints数组的数组
  • &amp;ar 的类型为 int (*)[2][2],即指向一个包含 2 个 ints 的数组的数组的指针。

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

所以,如果是这样的表达式

ar + 1

一样
(&(ar[0])) + 1;

基本上指向ar[1]

我不明白为什么ar + 1ar 的区别只有8 个字节

所以,这里的 “差异”ar[0] 的元素所占用的大小,即 2 ints,在您的平台上是 8 个字节。结果签出。

另一方面,对于像这样的表达式

&ar + 1;

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


注意:

引用C11,第 6.3.2.1 章

除非它是sizeof 运算符、_Alignof 运算符或 一元 &amp; 运算符,或者是用于初始化数组的字符串文字,该表达式具有 type ''array of type'' 被转换成一个类型为 ''pointer to type'' 的表达式,它指向 到数组对象的初始元素并且不是左值。 [....]

【讨论】:

    【解决方案2】:

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

    这个“数组衰减”规则在 C17 6.3.2.1 §3 中指定:

    除非它是 sizeof 运算符的操作数,或一元 & 运算符,或者是 用于初始化数组的字符串字面量,具有“类型数组”类型的表达式是 转换为类型为“pointer to type”的表达式,该表达式指向 数组对象并且不是左值

    因此,当您键入 &amp;ar 时,您会从数组衰减规则中获得特殊异常,不会发生衰减,但您实际上会按预期获得 int (*)[2][2]。因此&amp;ar + 1 给出了 16 个字节。

    【讨论】:

      【解决方案3】:

      所以:

      sizeof(int) == 4
      

      以下内容:

      int ar[2][2];
      

      是一个二维数组。

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

      所以:

      &ar[1]
      

      等于

      (ar + 1)
      

      这里ar“衰减”或“应调整”(读作:神奇地转换)为指针。指向两个 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)

      我不明白的是为什么 ar + 1 和 ar 之间的差异只有 8 个字节。

      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
      

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

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

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

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

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

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

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

      我认为你没有理解在二维数组的情况下,“第一”级是两个元素的一维数组,而第二个是一个 int。所以typeof(ar[0]) 是一个由两个 int 元素组成的数组。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-08-15
        • 1970-01-01
        • 2021-01-25
        • 2014-05-20
        • 1970-01-01
        • 2020-07-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多