Как C получает адрес строки для 2d массива
Может кто-нибудь объяснить мне, как C получает правильный адрес памяти для строки, когда вы используете только один индекс для доступа к 2d массиву?
Пример -
int array2D[2][2] = {1,2,3,4};
printf ( "starting address of row2 = %p" , array2D[1]);
Я понимаю, что при подписке на C то, что на самом деле происходит, это добавление указателя, поэтому для массива 1d имя массива указывает на элемент 0. В этом случае, если бы я хотел элемент 1, компилятор взял бы начальный адрес (скажем, 4000) и добавьте 4 к нему (предполагая 4-битное целое число), так что возвращаемым является элемент по адресу памяти 4004.
Насколько я понимаю, когда вы заполняете 2d массив, как в моем примере, они распределяются последовательно, поэтому я бы
1 2
3 4
по адресам
4000 4004
4008 4012
Так как же получается, что C в этом случае array2D[1] должен указывать на 4008, а не на 4004? Он запускает оператор sizeof() или я неправильно понял здесь фундаментальное значение?
заранее спасибо
4 ответа
C знает, какова длина каждой строки, поэтому он умножает, чтобы найти строку.
int x[][3] = {{1,2,3},{4,5,6}};
затем &x[1][0]
является &x[0][0]
плюс 3 * sizeof(int)
,
Вот почему в объявлении многомерного массива C должны быть указаны все измерения, кроме первого.
sizeof(array2D[1]) == 8;
если адрес array2D равен 4000;
поэтому адрес array2D [1] равен 4000+sizeof(array2D[1]) == 4000+8;
Арифметика указателя зависит от типа элемента, на который указывает. Дан указатель p
печатать T
, p + 1
указывает на следующий элемент типа T
, не обязательно следующий байт p
, Если T
является char
, затем p + 1
указывает на следующее char
объект после p
, который начинается с байта, следующего сразу p
; если T
является char [10]
, затем p + 1
указывает на следующий 10-элементный массив char
после p
, который начинается с 10-го байта следующего p
,
Тип выражения array2d
в это "2-элементный массив 2-элементный массив int
", который" распадается ", чтобы набрать" указатель на 2-элементный массив int
", или же int (*)[2]
1 Таким образом, выражение array2d[1]
интерпретируется как *(array2d + 1)
, поскольку array2d
указывает на объект типа int [2]
, array2d + 1
указывает на следующий 2-элементный массив int
следующий array2d
, который 2 * sizeof int
байтов от array2d
,
1. За исключением случаев, когда это операнд
sizeof
или одинарный &
операторы, или строковый литерал, используемый для инициализации другого массива в объявлении, выражение типа "массив N-элемента T
"будет преобразован в выражение типа" указатель на T
"и его значением будет адрес первого элемента в массиве.
Это будет немного скучно, но терпите меня до сих пор.
Подписка на массив - это просто сокращение: (p[N])
равняется (*(p + N))
во всех контекстах для типов указателей (оба являются недопустимыми выражениями для void*
, хоть).
Сейчас если p
это тип массива, он будет распадаться на тип указателя в выражении, как (*(p + N))
; int[2][2]
будет распадаться на указатель типа (*)[2]
(т.е. указатель на int[2]
).
Арифметика указателей учитывает типы; нам нужно преобразовать вещи в char*
чтобы визуализировать, что компилятор делает с нами:
T *p;
p[N] equals *(p + N) equals *(T*)((unsigned char*)p + N * sizeof *p)
Сейчас если T
были int[2]
(чтобы соответствовать ситуации, которую мы описали выше), то sizeof *p
было бы sizeof(int[2])
т.е. 2 * sizeof(int)
,
Так работает подписка в так называемых многомерных массивах.