Страницы

Поиск по вопросам

среда, 22 мая 2019 г.

Странности при работе с двумерным массивом в си

#include "stdio.h"
int main(void) { int a[3][2] = {{10,2}, {2,3}, {3,4}}; printf("%p
", a[0]); printf("%p
", (int *)a); printf("%p
", *a); printf("%p
", a); printf("%p
", &(a[0][0])); return 0; }
Код выдает одно и то же число... Особенно поражает то, что равны a и *a. Что происходит? И еще когда смотрю на *(int *)a, то получаю 10...


Ответ

Вас не должно удивлять равенство a и *a в данном контексте. Нечего удивительного в этом нет. В языке С "двумерный массив" - это просто обычный одномерный массив, элементами которого тоже являются одномерные массивы.
Одномерный массив, скажем, double d[10] - это просто плоский непрерывный компактный блок памяти, размера 10 * sizeof(double), состоящий из десяти плотно лежащих друг за другом элементов типа double. При этом адрес всего массива &d численно совпадает с адресом его нулевого элемента &d[0], ибо "начинаются" они в одной и той же точке памяти. Ситуация полностью аналогична равенству указателей вот в таком вот примере
struct S { int a; } s;
printf("%p %p
", (void *) &s, (void *) &s.a); // Оба указателя совпадают численно
Адрес всего объекта struct-типа численно совпадает с адресом самого первого поля внутри этого объекта. Совершенно аналогичным образом адрес всего массива численно совпадает с адресом самого первого (нулевого) элемента этого массива.
При этом в языке С выражение d типа "массив double [10]" в большинстве контекстов (не во всех) неявно приводится к типу double * - указателю, указывающему на нулевой элемент массива. Т.е. в большинстве контекстов выражение d ведет себя эквивалентно выражению &d[0], т.е. массив внешне ведет себя как указатель. Это явление называют array type decay. (Именно по этой причине массивы в С часто путают с указателями, хотя на самом деле это разные типы и никаких указателей в массивах нет.)
Все это немедленно применимо и к двумерным массивам (и многомерным массивам), ибо, как сказано выше, в языке С "двумерный массив" - это рекурсивная по своему устройству структура: это просто одномерный массив, элементами которого являются одномерные массивы. Вышеупомянутое явление array type decay работает неизменным образом на любом уровне этой рекурсии.
Выше выражение a изначально имеет тип int [3][2], но в данном контексте неявно приводится к типу "указатель на нулевой элемент массива a". Этот указатель имеет тип int (*)[2] и указывает на подмассив a[0] типа int [2] в составе a
Выражение *a изначально имеет тип int [2] - это подмассив a[0] в составе a, но в данном контексте *a неявно приводится к типу "указатель на нулевой элемент массива a[0]". Это указатель типа int *, который указывает на самый первый int в составе подмассива a[0], т.е. на a[0][0]
И сам a, и a[0], и a[0][0] начинаются в одной и той же точке памяти, по каковой причине числовые представления этих указателей совпадают
printf("%p %p %p
", (void *) &a, (void *) a, (void *) *a); // Все три указателя совпадают численно

Комментариев нет:

Отправить комментарий