Понимание выделенных адресов памяти в элементах массива в C (gcc в Windows 10) - PullRequest
0 голосов
/ 26 ноября 2018

Я пытаюсь справиться с указателями и массивами в 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 + 1и ar составляет всего 8 байтов.Это будет означать, что массив может содержать только 2 int с 4 байта.

У меня такая же проблема с пониманием ar[0] и ar[1], как вы можете видеть в моем коде.

Разве ar + 1 и &ar + 1 не должны давать одинаковый результат?

Ответы [ 3 ]

0 голосов
/ 26 ноября 2018

ar, когда используется в выражении, «распадается» на указатель на первый элемент.В этом случае arr + 1 дает арифметику для указателя типа int (*)[2].Который указывает на int [2] с размером 8 байт.

Это правило "распада массива" указано в C17 6.3.2.1 §3:

За исключением случаев, когда это операндоператора sizeof, или унарного оператора &, или строкового литерала, используемого для инициализации массива, выражение с типом '' массив типа '' преобразуется в выражение с типом '' указатель на тип '', которое указываетк начальному элементу объекта массива и не является lvalue

Поэтому, когда вы набираете &ar, вы получаете специальное исключение из правила затухания массива, затухание не происходит, но вы фактически получаетеint (*)[2][2] как и ожидалось.И поэтому &ar + 1 дает 16 байтов.

0 голосов
/ 26 ноября 2018

Итак:

sizeof(int) == 4

Следующее:

int ar[2][2];

- это двумерный массив.

Мы знаем, что 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

Поскольку ar равно int[2][2], тогда *ar равно int[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], т.е.это указатель на двумерный массив.

Поскольку тип изменяется, арифметика указателя изменяется.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); разыграть ваши указатели.

0 голосов
/ 26 ноября 2018

В вашем случае ar - это массив.Следовательно, прежде всего, помните,

  • ar является типом int [2][2], который является массивом массивов int s
  • &ar имеет тип int (*)[2][2], т. Е. Указатель на массив массива 2 int с.

При этом тип массива в некоторых случаях затухает до указателя на первый элемент массива. Примечание

Итак, в случае выражения типа

ar + 1

точно так же, как

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

, которое в основномуказывает на ar[1].

Я не понимаю, почему разница между ar + 1 и ar составляет всего 8 байтов

Итак, «разница» здесь - это размер, занимаемый элементами ar[0], то есть 2 ints, что на вашей платформе составляет 8 байтов.Результат проверяется.

С другой стороны, для выражения типа

&ar + 1;

он работает с типом указателя (как упомянуто ранее) и указывает на местоположение, расположенное за последним элементом.в массиве.Таким образом, разница для 2 массивов по 2 int с, следовательно, (2 * 2 * 4) = 16 байтов.


Примечание:

Цитирование C11, глава §6.3.2.1

За исключением случаев, когда это операнд оператора sizeof, оператора _Alignof или унарный *Оператор 1064 * или строковый литерал, используемый для инициализации массива; выражение с типом '' массив типа '' преобразуется в выражение с типом '' указатель на тип '', который указывает на начальный элемент объекта массива и не является lvalue.[....]

...