Что такое имя массива в 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
  1. Что именно это делает 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. Это сокращение, которое компилятор обрабатывает для вас.