Имя массива в Си: адрес или указатель первого элемента
Подробное объяснение поведения имени массива в Си. Разбираем преобразование массива в указатель, различия между именем массива и указателем, особенности работы с двумерными массивами.
В языке Си является ли имя массива адресом его первого элемента или указателем на первый элемент? Или указатель создается только при необходимости (например, при арифметике указателей)? Или оба варианта верны?
Рассмотрим пример:
int arrOne[5] = {1,2,3,4,5};
Здесь arrOne - это адрес первого элемента? Или указатель на первый элемент? Или он преобразуется в указатель только когда это необходимо (например, при арифметике адресов)? Или оба варианта верны?
Отдельный вопрос касается двумерных массивов:
int arrTwo[3][2] = {{1,2}, {3,4}, {5,6}};
В этом случае arrTwo - это адрес первого элемента или указатель на первый элемент, то есть {1,2}? Получается, arrTwo - это адрес самого первого элемента {1,2} или адрес первого элемента первого подмассива, то есть {1}?
В языке Си имя массива ведет себя как указатель на первый элемент, но это не совсем точно описывает всю ситуацию. Ответ на ваш вопрос сложнее и зависит от контекста использования. Давайте разберем это подробно.
Содержание
- Основной принцип: имя массива как указатель
- Преобразование массива в указатель (decay)
- Различия между именем массива и указателем
- Двумерные массивы
- Практические примеры
- Заключение
Основной принцип: имя массива как указатель
В языке Си имя массива является адресом первого элемента и ведет себя как константный указатель на этот элемент. Это фундаментальное правило языка.
Для примера:
int arrOne[5] = {1,2,3,4,5};
Здесь:
arrOneпредставляет собой адрес первого элемента массива (&arrOne[0])arrOneведет себя как константный указатель типаint*, который указывает на первый элемент- Значение
arrOneравно значению&arrOne[0]
Как объясняется в Engineering LibreTexts:
An array name contains the address of first element of the array which acts like constant pointer. It means, the address stored in array name can’t be changed.
Scaler Topics также подтверждает:
Array name itself acts as a pointer to the first element of the array and also if a pointer variable stores the base address of an array…
Преобразование массива в указатель (decay)
Ключевой концепцией является array decay (преобразование массива в указатель). Это происходит автоматически, когда имя массива используется в выражениях.
Согласно GeeksforGeeks:
In C, the array decays to pointers. It means that array decay is the process in which an array gets converted to a pointer.
Learn C++ дает более подробное объяснение:
In most cases, when a C-style array is used in an expression, the array will be implicitly converted into a pointer to the element type, initialized with the address of the first element (with index 0). Colloquially, this is called array decay (or just decay for short).
Когда происходит преобразование:
- При передаче массива в функцию
- При использовании в арифметике указателей
- При присваивании указателю
- Во многих других выражениях
Важно: Преобразование происходит автоматически при необходимости, а не всегда. В некоторых контекстах имя массива сохраняет свои свойства массива.
Различия между именем массива и указателем
Хотя имя массива ведет себя как указатель, есть важные отличия:
| Характеристика | Имя массива | Указатель |
|---|---|---|
| Размер | sizeof возвращает размер всего массива |
sizeof возвращает размер указателя |
| Изменяемость | Константное выражение - адрес нельзя изменить | Адрес можно изменять |
| Адрес | &arrOne дает адрес самого массива |
&ptr дает адрес переменной-указателя |
Как объясняется в Stack Overflow, это создает путаницу:
*If you create a pointer, you can then take its address: int *p = array; int *pp = &p;…
Двумерные массивы
Для двумерных ситуация становится интереснее. Рассмотрим:
int arrTwo[3][2] = {{1,2}, {3,4}, {5,6}};
Здесь:
arrTwo- это адрес первого элемента первого подмассива, то есть адресarrTwo[0][0](элемента{1})arrTwoведет себя как указатель на массив из 2 элементов типаint, то естьint (*)[2]arrTwo[0]- это первый подмассив, который сам преобразуется в указатель на свой первый элемент&arrTwo[0][0]
Согласно cppreference.com:
Note that when array-to-pointer decay is applied, a multidimensional array is converted to a pointer to its first element (e.g., a pointer to its first row…)
Для двумерного массива:
arrTwo→&arrTwo[0](указатель на первый подмассив)arrTwo[0]→&arrTwo[0][0](указатель на первый элемент первого подмассива)
Практические примеры
int arrOne[5] = {1,2,3,4,5};
// Все эти выражения эквивалентны:
arrOne == &arrOne[0] // true
*(arrOne + i) == arrOne[i] // true для любого i
Когда преобразование НЕ происходит:
sizeof(arrOne); // Размер всего массива (20 байт для int[5])
sizeof(&arrOne[0]); // Размер указателя (8 байт на 64-битной системе)
Для двумерного массива:
int arrTwo[3][2] = {{1,2}, {3,4}, {5,6}};
arrTwo == &arrTwo[0] // true (адрес первого подмассива)
arrTwo[0] == &arrTwo[0][0] // true (адрес первого элемента первого подмассива)
Заключение
Ответ на ваш вопрос: оба варианта верны, но с важными нюансами:
-
Имя массива всегда представляет собой адрес первого элемента и ведет себя как константный указатель на этот элемент
-
Преобразование в указатель происходит автоматически (array decay) в большинстве выражений, но не всегда
-
Для двумерных массивов:
arrTwo- это адрес первого элемента первого подмассива (&arrTwo[0][0])arrTwoведет себя как указатель на массив (int (*)[2])- В большинстве выражений
arrTwoпреобразуется в&arrTwo[0](указатель на первый подмассив)
Как отмечено в C FAQ:
When people speak casually of a pointer to an array, they usually mean a pointer to its first element. Instead of a pointer to an array, consider using a pointer to one of the array’s elements.
Понимание этих тонкостей критически важно для правильной работы с массивами и указателями в Си.
Источники
- Engineering LibreTexts - Array Name as Pointers
- Scaler Topics - Array of Pointers in C
- GeeksforGeeks - Array Decay In C
- Learn C++ - C-style array decay
- Stack Overflow - How come an array’s address is equal to its value in C?
- C FAQ - Arrays and Pointers
- cppreference.com - Array-to-pointer decay
- James Fisher - What is ‘array decaying’ in C?