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

时间:2018-11-26 10:30:05

标签: c arrays memory pointer-arithmetic

我试图掌握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个int,在我的系统上为4x4 = 16个字节。我猜这也是为什么&ar + 1&ar之间的字节差为(hex)30-20 = 16的原因。

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

我在理解ar[0]ar[1]时遇到了同样的问题。

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

3 个答案:

答案 0 :(得分:2)

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

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

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

因此,如果表达式类似

ar + 1

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

,它基本上指向ar[1]

  

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

因此,这里的“差异” ar[0]元素所占据的大小(即2 ints)组成,在您的平台中, 8个字节。结果检出。

另一方面,对于类似

的表达式
&ar + 1;

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


注意:

引用C11,第6.3.2.1章

  

除非它是sizeof运算符,_Alignof运算符或   一元&运算符,或者是用于初始化数组的字符串文字,该表达式具有   类型“类型数组” 转换为类型为“类型指针” 的表达式   数组对象的初始元素,不是左值。 [....]

答案 1 :(得分:2)

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

此“阵列衰减”规则在C17 6.3.2.1§3中指定:

  

除非它是sizeof运算符的操作数或一元&运算符,或者是   用于初始化数组的字符串文字,其类型为“数组类型”的表达式为   转换为类型为“要输入的指针”的表达式,该表达式指向的初始元素   数组对象,不是左值

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

答案 2 :(得分:0)

所以:

sizeof(int) == 4

以下内容:

int ar[2][2];

是2D数组。

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

所以:

&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指针值等于&array指针值。这是C的怪癖,将address-of运算符应用于数组会导致指向同一内存的数组指针。到array的类型为int[2][2],但是&array的类型为int(*)[2][2],即。它是指向2d数组的指针。

由于类型更改,指针算法也会更改。 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,因此增加指针时指针值的差异。

我认为您无法掌握2d数组的情况,“第一”级别是两个元素的1d数组,而第二个级别是int。因此typeof(ar[0])是由两个int元素组成的数组。

您的代码包含UB,因为%p修饰符应仅与void*指针一起使用。最好记住(或至少知道应该这么做)printf("%p", (void*)&ar[1][0] + 1);投射指针。