Что такое имя массива в c?

Мне сложно понять тип и использование имени массива в C. Это может показаться длинным, но, пожалуйста, несите меня.

Я понимаю, что следующий оператор объявляет, a он имеет тип int [] т.е. массив целых чисел .

 int a[30]; 

В то время как a также указывает первый элемент массива, и такие вещи, как *(a+2) , действительны. Таким образом, вы видите, как указатель на целое число . Но на самом деле типы int [] и int* различны; в то время как первый тип массива, а позже является указателем на целое число .

Также переменная типа int [] преобразуется в переменную типа int* при передаче ее функциям; как и в массивах C , передаются по ссылке (за исключением оператора sizeof ).

Здесь приходит точка, которая заставляет меня болтаться. Посмотрите на следующий fragment кода:

 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 

Итак, как работает вышеуказанный код? У меня есть два вопроса:

  1. a и &a имеют одинаковые значения. Зачем?
  2. Что именно делает 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 или & nary, или если объект является строковым литералом, инициализирующим массив.

Например, это утверждение:

 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); 

Другие ответы уже объяснили этот вопрос. Я пытаюсь объяснить это диаграммой. Надеюсь, это поможет.


Когда вы объявляете массив

 int a[3] = {5, 4, 6} 

расположение памяти выглядит

введите описание изображения здесь

Теперь отвечая на ваш вопрос:

  1. a и &a имеют одинаковые значения. Как?

Поскольку вы уже знаете, что a имеет тип массива и имя массива, a становится указателем на первый элемент массива a (после распада), то есть он указывает на адрес 0x100 . Обратите внимание, что 0x100 также является начальным адресом блока памяти (массив a ). И вы должны знать, что, как правило, адрес первого байта называется адресом переменной . То есть, если переменная имеет значение 100 байт, то ее адрес равен адресу своего первого байта.

&a – адрес всего блока памяти, то есть это адрес массива a . См. Диаграмму:

введите описание изображения здесь

Теперь вы можете понять, почему a и &a оба имеют одинаковое значение адреса, хотя оба имеют разный тип.

Что именно это делает int (*p)[3]; Объявляет указатель на массив, я знаю это. Но как указатель на массив отличается от указателя на первый элемент массива и имя массива?

См. Приведенный выше рисунок, он ясно объясняет, как указатель на массив отличается от указателя на элемент массива.
Когда вы назначаете &a на p , p указывает на весь массив, имеющий начальный адрес 0x100 .


ПРИМЕЧАНИЕ. Что касается линии

… как и в массивах C , передаются по ссылкам (за исключением функции sizeof ).

В C аргументы передаются по значению. Нет прохода по ссылке в C. Когда обычная переменная передается функции, ее значение копируется ; любые изменения соответствующего параметра не влияют на переменную.
Массивы также передаются по значению, но разница в том, что имя массива распадается на указатель на первый элемент, и этот указатель присваивается параметру (здесь, значение указателя копируется) функции; сам массив не копируется.
В отличие от обычной переменной, массив, используемый в качестве аргумента, не защищен от каких-либо изменений, так как никакой копии не делается из самого массива , вместо этого создается копия указателя на первый элемент.

Вы также должны заметить, что sizeof не является функцией, а имя массива не является аргументом в этом случае. sizeofоператор, а имя массива – операнд . То же самое верно, когда имя массива является операндом унарного оператора & .

  1. a соответствует указателю, указывающему на 0-й элемент массива. Принимая во внимание то же самое в случае с & a.It просто дает начальный адрес массива.

Как, a --> pointer pointing to starting element of array a[],it does not know about other element's location. ,

&a --->address location for storing array a[] which stores first element location,but knows every element's location .

Аналогичным образом, расположение других элементов будет (a + 2), (a + 4) и, следовательно, до конца массива.

Следовательно, вы получили такой результат.

  1. int (* p) [3] – указатель на массив. если бы это было int * p [3], это означало бы совсем другое. Это означало бы массив указателей, которые были бы полностью отличны от этого контекста.

Указатель на массив автоматически позаботится обо всех остальных элементах массива. В этом случае ваш (p);

В то время как указатель на первый элемент массива, т. Е. A, будет знать только о первом элементе массива. Вам придется вручную указывать арифметические направления указателя для доступа к следующим элементам. Посмотрите, в этом случае — мы можем получить второй элемент из a, добавив 2 к a, т.е. a + 2, третий элемент, добавив 4 к a, т. е. a + 4 и т. д. // разумеем разницу двух, поскольку это целочисленный массив!

В ответ на вопрос 1 это просто аспект языка C, как он был разработан, в отличие от большинства других современных языков. C / C ++ позволяет напрямую манипулировать адресами в памяти и имеет встроенные средства для «понимания» этого. Есть много статей в Интернете, которые объясняют это лучше, чем я мог в этом небольшом пространстве. Вот один, и я уверен, что есть много других: http://www.cprogramming.com/tutorial/c/lesson8.html

От стандарта C99 n1124 6.3.2.1 p3

За исключением случаев, когда это операнд оператора sizeof или унарный оператор & или строковый литерал, используемый для инициализации массива, выражение, которое имеет тип ” array of type ”, преобразуется в выражение с указателем типа ” to type ”, который указывает на начальный элемент объекта массива и не является значением lvalue. Если объект массива имеет class хранения регистров, поведение не определено.

a и & a имеют одинаковое значение, потому что давно вам нужно было использовать адресный оператор & на массивах для получения адреса массива, но он больше не нужен. Имя массива (в этом случае) в наши дни просто представляет собой адрес памяти самого массива, который также вы получаете от & a. Это сокращение, которое компилятор обрабатывает для вас.

  • Функции хранилища C # в словаре
  • Использование стрелки (->) в C
  • Неинициализированные указатели в коде
  • Что происходит, когда вы удаляете указатель дважды или больше на C ++?
  • Размещение звездочки в объявлениях указателей
  • Как печатать переменные адреса в C?
  • Почему бы не использовать указатели для всего на C ++?
  • Почему не указатели инициализируются с помощью NULL по умолчанию?
  • Возвращаемый массив в функции
  • Параметр, проходящий в C - указатели, адреса, псевдонимы
  • Инициализация указателя в отдельной функции в C
  • Давайте будем гением компьютера.