Подтвердить что ты не робот

C/С++ int [] vs int * (указатели и обозначения массива). В чем разница?

Я знаю, что массивы в C - это просто указатели на последовательно сохраненные данные. Но какие различия подразумевают различие в обозначениях [] и *. Я имею в виду во ВСЕХ возможных контекстах использования. Например:

char c[] = "test";

если вы предоставите эту инструкцию в теле функции, она выделит строку в стеке, пока

char* c = "test";

будет указывать на сегмент данных (только для чтения).

Можете ли вы перечислить все различия между этими двумя обозначениями во всех контекстах использования, чтобы сформировать четкое общее представление.

4b9b3361

Ответ 1

В соответствии со стандартом C99:

Тип массива описывает смежно распределенный непустой набор объекты с определенным типом объекта-члена, называемым элементом type.36) Типы массивов характеризуются их типом элемента и количество элементов в массиве. Тип массива называется полученный из его типа элемента, и если его типом элемента является T, массив тип иногда называют "массивом" Т. Конструкция массива тип из типа элемента называется "выводом типа массива".


Тип указателя может быть выведен из типа функции, типа объекта или неполный тип, называемый ссылочным типом. Тип указателя описывает объект, значение которого предоставляет ссылку на объект ссылочный тип. Тип указателя, полученный из ссылочного типа T иногда называют "указателем на Т.". Построение указателя тип из ссылочного типа называется "выводом типа указателя".

В соответствии со стандартными объявлениями

char s[] = "abc", t[3] = "abc";
char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };

идентичны. Содержимое массивов может быть изменено. С другой стороны, декларация const char *p = "abc"; определяет p с указателем типа '' to constant char и инициализирует его, указывая на объект с типом '' постоянный массив char (в С++) с длиной 4, элементы которого инициализируются литералом строковой буквы. Если делается попытка использовать p для изменения содержимого массива, поведение undefined.

В соответствии с 6.3.2.1 Array subscripting разыменование и индексирование массива идентичны:

Определение индексного оператора [] состоит в том, что E1 [E2] идентичен (* ((E1) + (E2))).

Различия между массивами и указателями:

  • указатель не имеет информации о размере памяти за ним (нет никакого переносного способа его получения)
  • массив неполного типа не может быть построен
  • тип указателя может быть получен из неполного типа
  • указатель может определять рекурсивную структуру (этот результат является следствием двух предыдущих)

Эти ссылки могут быть полезны для пользователя:

Ответ 2

char c[] = "test";

Это создаст массив, содержащий строковый тест, чтобы вы могли изменять/изменять любой символ, скажем

c[2] = 'p';

но

char * c = "test"

Это строковый литерал - это const char.
Поэтому всякая модификация этого строкового литерала дает нам segfault. Итак,

c[2] = 'p';

теперь незаконно и дает нам segfault.

Ответ 3

char [] обозначает тип "массив неизвестной границы char", а char * обозначает тип "указатель на char". Как вы заметили, когда определение переменной типа "массив неизвестной границы char" инициализируется строковым литералом, тип преобразуется в "array [N] of char", где N является соответствующий размер. То же самое относится и к инициализации из агрегата массива:

int arr[] = { 0, 1, 2 };

arr преобразуется в тип "array [3] of int".

В определяемом пользователем определении типа (struct, class или union) типы с привязкой к массивам с неизвестными в С++ запрещены, хотя в некоторых версиях C они разрешены как последний элемент структуры, где они могут использоваться для доступа к выделенной памяти за конец структуры; это использование называется "гибкими массивами".

Рекурсивная конструкция типа - еще одно отличие; можно построить указатели и массивы char * (например, char **, char (*)[10]), но это незаконно для массивов неизвестной привязки; нельзя писать char []* или char [][10] (хотя char (*)[] и char [10][] в порядке).

Наконец, cv-квалификация действует по-разному; заданные typedef char *ptr_to_char и typedef char array_of_unknown_bound_of_char[], cv-qualifiying версия указателя будет вести себя так, как ожидалось, тогда как cv-qualifying версия массива будет переносить cv-квалификацию на тип элемента: то есть const array_of_unknown_bound_of_char эквивалентно const char [] а не вымышленный char (const) []. Это означает, что в определении функции, где распад матрицы к указателю работает с аргументами до создания прототипа,

void foo (int const a[]) {
    a = 0;
}

является законным; нет способа сделать параметр, не связанный с неизвестной границей, немодифицируемым.

Ответ 4

Весь смысл становится понятным, если вы знаете, что

Declaring a pointer variable does not create the type of variable, it points at. It creates a pointer variable.

Поэтому при использовании, если вам нужна строка, вам нужно указать массив символов, а позже указатель можно использовать.

Ответ 5

На самом деле массивы эквивалентны постоянным указателям.

Кроме того, char c [] выделяет память для массива, базовым адресом которого является сам c. Для хранения этого адреса не выделяется отдельная память.

Запись char * c выделяет память для строки, базовый адрес которой хранится в c. Кроме того, для хранения c используется отдельная ячейка памяти.