Что такое имя массива в c?
Мне сложно понять тип и использование имени массива в C. Это может показаться длинным, но, пожалуйста, несите меня.
Я понимаю, что следующий оператор объявляет a
типом int []
i.e массив целых чисел.
int a[30];
Пока a
также указывает первый элемент массива, и такие вещи, как *(a+2)
, действительны. Таким образом, сделать a
похожим на указатель на целое число. Но на самом деле типы int []
и int*
различны; в то время как первый тип массива, а позже - указатель для целого числа.
Также переменная типа int []
преобразуется в переменную типа int*
при передаче ее функциям; как в C
массивы передаются по ссылке (за исключением оператора sizeof
).
Вот точка, которая заставляет меня болтаться. Посмотрите следующий фрагмент кода:
int main()
{
int (*p)[3];
int a[3] = { 5, 4, 6 };
p = &a;
printf("a:%d\t&a:%d\n",a,&a);
printf("%d",*(*p + 2));
}
ВЫВОД:
a:2686720 &a:2686720
6
Итак, как работает вышеуказанный код? У меня есть два вопроса:
-
a
и &a
имеют одинаковые значения. Почему?
- Что именно делает
int (*p)[3];
? Он объявляет указатель на массив, я это знаю. Но как указатель на массив отличается от указателя первым элементом массива и именем массива?
Может ли кто-нибудь прояснить ситуацию? У меня много путаницы.
Я знаю, что я должен использовать %p
в качестве заполнителя вместо использования %d
для печати значения переменных указателя. Поскольку использование целочисленного заполнителя может печатать усеченные адреса. Но я просто хочу, чтобы все было просто.
Ответы
Ответ 1
- a и & a имеют одинаковые значения. Как?
Они имеют одинаковое значение, но разные типы. Объекты массива не имеют прокладки между элементами (до или после), поэтому адрес массива и адрес первого элемента массива совпадают.
То есть:
(void *) a == (void *) &a
- Что именно это делает int (* p) [3]; Объявляет указатель на массив, я знаю это. Но как указатель на массив отличается от указателя на первый элемент массива и имя массива?
Это два разных типа указателей. Возьмем, например, арифметику указателя:
a + 1 /* address of the second element of the array */
&a + 1 /* address one past the last element of the array */
EDIT: из-за популярного спроса я добавил ниже некоторую информацию о преобразовании массивов.
С тремя исключениями в выражении объект массива типа T
преобразуется в значение типа указатель на T
, указывающий на первый элемент массива. Исключения составляют, если объектом является операнд унарного оператора sizeof
или &
или если объект является строковым литералом, инициализирующим массив.
Например, это утверждение:
printf("a:%d\t&a:%d\n", a, &a);
фактически эквивалентен:
printf("a:%d\t&a:%d\n", &a[0], &a);
Также обратите внимание, что d
спецификатор преобразования может использоваться только для печати целого числа со знаком; для печати значения указателя вы должны использовать спецификатор p
(и аргумент должен быть void *
). Итак, чтобы правильно использовать вещи:
printf("a:%p\t&a:%p\n", (void *) a, (void *) &a);
соответственно:
printf("a:%p\t&a:%p\n", (void *) &a[0], (void *) &a);
Ответ 2
Другие ответы уже объяснили проблему. Я пытаюсь объяснить это диаграммой. Надеюсь, это поможет.
Когда вы объявляете массив
int a[3] = {5, 4, 6}
расположение памяти выглядит как
![enter image description here]()
Теперь отвечая на ваш вопрос:
-
a
и &a
имеют одинаковые значения. Как?
Как вы уже знаете, a
имеет тип массива, а имя массива a
становится указателем на первый элемент массива a
(после распада), то есть указывает на адрес 0x100
. Обратите внимание, что 0x100
также является начальным адресом блока памяти (массив a
). И вы должны знать, что в целом адрес первого байта называется адресом переменной. То есть, если переменная имеет значение 100 байт, то ее адрес равен адресу своего первого байта.
&a
- адрес всего блока памяти, т.е. это адрес массива a
. См. Диаграмму:
![enter image description here]()
Теперь вы можете понять, почему a
и &a
имеют одинаковое значение адреса, хотя оба имеют различный тип.
Что именно это делает int (*p)[3];
Объявляет указатель на массив, я знаю это. Но как указатель на массив отличается от указателя на первый элемент массива и имя массива?
См. приведенный выше рисунок, он ясно объясняет, как указатель на массив отличается от указателя на элемент массива.
Когда вы назначаете &a
в p
, тогда p
указывает на весь массив, имеющий начальный адрес 0x100
.
ПРИМЕЧАНИЕ. Относительно строки
... как в C
массивы передаются по ссылкам (за исключением функции sizeof
).
В C аргументы передаются по значению. Нет прохода по ссылке в C. Когда обычная переменная передается функции, ее значение копируется; любые изменения соответствующего параметра не влияют на переменную.
Массивы также передаются по значению, но разница в том, что имя массива распадается на указатель на первый элемент, и этот указатель присваивается параметру (здесь, значение указателя копируется) функции; сам массив не копируется.
В отличие от обычной переменной, массив, используемый в качестве аргумента, не защищен от каких-либо изменений, так как никакой копии не делается из самого массива, вместо этого создается копия указателя на первый элемент.
Вы также должны заметить, что sizeof
не является функцией, а имя массива в этом случае не является аргументом. sizeof
- оператор, а имя массива - операнд. То же самое верно, когда имя массива является операндом унарного оператора &
.
Ответ 3
- a соответствует указателю, указывающему на 0-й элемент массива. В то время как то же самое происходит с & a.It просто дает начальный адрес массива.
В, a --> pointer pointing to starting element of array a[],it does not know about other element location.
.
&a --->address location for storing array a[] which stores first element location,but knows every element location
.
Аналогично, расположение других элементов будет (a + 2), (a + 4) и, следовательно, до конца массива.
Следовательно, вы получили такой результат.
- int (* p) [3] - указатель на массив. если бы это было int * p [3], это означало бы совсем другое. Это означало бы массив указателей, который бы полностью отличался от этого контекста.
Указатель на массив автоматически позаботится обо всех других элементов в массиве. В этом случае ваш (p);
В то время как указатель на первый элемент массива, то есть, знаю только о первом элементе массива. Вам придется вручную дать указателю арифметические указания для доступа к следующим элементам. См., в этом case --- мы можем получить второй элемент из a, добавив 2 к a, т.е. a + 2, третий элемент, добавляя 4 к a, то есть a + 4 и так далее.//уметь разность двух, поскольку это целочисленный массив!
Ответ 4
В ответе на вопрос 1 это просто аспект языка C как разработанный, в отличие от большинства других современных языков. C/С++ позволяет напрямую манипулировать адресами в памяти и имеет встроенные средства для "понимания" этого. Есть много статей в Интернете, которые объясняют это лучше, чем я мог в этом небольшом пространстве. Вот один, и я уверен, что есть много других: http://www.cprogramming.com/tutorial/c/lesson8.html
Ответ 5
От C99 Standard n1124 6.3.2.1 p3
За исключением случаев, когда это операнд оператора sizeof или унарный & оператор или является строковым литералом, используемым для инициализации массива, выражение, которое имеет тип '' массив типа, преобразуется в выражение с типом '' указатель на тип, указывающий на начальный элемент объекта массива и не является значением lvalue. Если объект массива имеет класс хранения регистров, поведение не определено.
Ответ 6
a и & a имеют одно и то же значение, потому что давно вам нужно было использовать оператор адреса и массивы для получения адреса массива, но он больше не нужен. Имя массива (в этом случае) в эти дни просто представляет собой адрес памяти самого массива, который также вы получаете от & a. Это сокращение, которое компилятор обрабатывает для вас.