Идентификатор массива указывает адрес памяти, начиная с которого он расположен, т.е. адрес его первого элемента. Работа с массивами тесно связана с применением указателей.
Пусть объявлен массив a из 5 целочисленных элементов:
int a[5];
a
a[0] | a[1] | a[2] | a[3] | a[4] |
4000 4004 4008 4012 4016
Здесь приведено символическое изображение оперативной памяти, выделенной компилятором для объявленного целочисленного массива а[5]. Адрес массива выбирается компилятором в зависимости от размера доступной памяти, наличия других переменных и массивов и др. Для конкретности, здесь положено значение адреса, равное 4000. В реальной программе вместо 4000 может быть другое значение, но относительное положение элементов массива всегда остается постоянным.
В языке С идентификаторы массивов считаются константными указателями (т.е. в данном примере а "имеет значение" 4000). Такую константу можно присвоить переменной типа указатель, но нельзя подвергать преобразованиям, например:
int a[5], *q;
q = a; // Правильно - присваивание константы переменной
a = q; // Ошибка: в левой части - указатель-константа
Именно потому, что имена массивов считаются константными указателями, в языке Си нельзя непосредственно присваивать массивы друг другу (хотя структуры, включающие массивы как поля, целиком присваивать друг другу можно!)
Однако операция sizeof для массивов все же дает размер массива, а не указателя:
int n = sizeof(a) / sizeof(*a);
// n=5, т.к. sizeof(a)=20, sizeof(int)=4
int m = sizeof(q) / sizeof(*q);
// m=1, т.к. sizeof(int*)=4, sizeof(int)=4