В чем разница между типом ** name и типом * name[]?

#c #c

#c #c

Вопрос:

В чем разница между типом ** name и типом * name[]?

Зачем кому-то использовать одно поверх другого?

Спасибо

Комментарии:

1. Фактически нет никакой разницы. Например, мы всегда видим, что char **argv и char *argv[] взаимозаменяемо используются для объявления аргументов main в программе на C. Вы можете выбрать один из них по соображениям ясности или согласованности.

Ответ №1:

Ну, это зависит от того, находится ли это в объявлении переменной или в аргументе функции? Если в объявлении переменной:

 Type** name = amp;pointer_to_type;
Type* name[] = { amp;pointer_to_type, 0, amp;pointer_to_type };
  

Первый — это указатель на указатель на тип, а второй — массив указателей на тип длиной 3.

Если в аргументе функции, это одно и то же. Массивы распадаются на указатели, и оба Type** name и Type* name[] точно такие же, как аргументы функции. Однако вторая форма дает понять, что name представляет собой массив указателей неизвестной длины, в то время как первая этого не делает. Я бы использовал Type** для указания одного элемента и Type*[] для указания массива.

Комментарии:

1. Я боюсь тебя, маленькая лошадка. Участник на 32 дня и 6000 репутации. Что это за метод?

2. Также хотелось бы добавить немного о распределении памяти: использование Type* name[] в качестве объявления переменной автоматически дает вам допустимую память при первом разыменовании (массив указателей размещается в стеке). Вы могли бы использовать эти команды последовательно: Type* name[4]; name[3] = NULL; но если бы вы попробовали это с Type** name помощью, вы бы выполнили segfault, если бы вы вручную не указали на valid Type* .

3. @sidyll: Энтузиазм, я бы предположил? У меня даже есть значок для этого: P

4. Ну, и у меня есть значок фанатика 🙂 В любом случае, поздравляю вас с вашими усилиями и талантом отвечать с точностью.

5. @sidyll, спасибо за смех… Я тоже не мог не заметить, что маленькая лошадка в огне. @K, вот тебе еще один значок!

Ответ №2:

Разница между ними в основном проявляется при объявлении / определении объектов любого типа.

Обозначение Type *name[] создает массив неизвестного размера (может быть выведено из инициализатора), Type** name создает указатель. Это означает:

 char *array[]={"hello", "world", 0}; /* OK, array of size 3 */
char **ptr={"hello", "world", 0};    /* not possible */
  

В некоторых выражениях они ведут себя по-разному. В частности, массивы не могут быть назначены, но переменные указателя могут:

 ptr  , ptr=array; /* assignment and mutation of ptr possible */
// array=whatever /* impossible */
  

Оператор sizeof работает по-разному на двух. sizeof(array) будет зависеть от количества элементов массива (в данном случае может быть 12), но sizeof(ptr) возвращает всегда один и тот же размер (например, 4 на основных 32-разрядных архитектурах)

Кроме того, при объявлении глобальных переменных вы не должны смешивать их:

 extern char* data[];
  

должно сопровождаться в .c файле

 char* data[N];
  

и наоборот. По сути, определение массива означает выделение нескольких последовательных объектов, тогда как определение указателя означает выделение одной переменной. Компилятор обрабатывает оба по-разному и должен знать, что есть что.

Однако при объявлении или передаче параметров функциям они одинаковы. Итак

 int main(int argc, char** argv)
int main(int argc, char* argv[]) /* the same */
  

Комментарии:

1. » массивы не являются значениями lvalues «, они являются значениями lvalues. » таким образом, они не могут быть назначены «, и это все равно не будет следовать: (x не является значением lvalue) не подразумевает («x = что угодно;» неверно сформирован). C — не простой язык. 😉

Ответ №3:

Зависит от контекста.

Если он определяет переменную, которая не является параметром функции, то in Type** name name это указатель на указатель на переменную типа Type , а in Type* name[SOME_POSITIVE_INTEGER_CONSTANT] — это массив указателей на переменные типа Type .

Если это параметр функции, то оба они одинаковы и name являются указателем на указатель на переменную типа Type .

Комментарии:

1. В объявлении переменной, которое не является определением, вы можете опустить SOME_POSITIVE_INTEGER_CONSTANT

2. @curiousguy: в области действия файла «int * ga[];» выдает «предупреждение: массив ga' assumed to have one element". In function scope "int* ma[];" results in "error: array size missing in ma'». Вы хотите избежать таких ситуаций. однако «extern int * ega[];» в порядке. Это с помощью gcc (3.3.4), скомпилированного как C со всеми включенными предупреждениями.

3. Не существует такого понятия, как «область действия файла». Вы, вероятно, имеете в виду глобальную область. В любой области объявление переменной без спецификатора класса хранения extern также является определением .

Ответ №4:

По сути, тип ** — это указатель на указатель. Подумайте, что это похоже на (Тип *) * . Таким образом, он указывает на тип *, который может быть типом или типом [] .

А другой, Type * — это указатель на тип или, в данном случае, на тип массива[]. Таким образом, они «почти» одинаковы.

Комментарии:

1. Пожалуйста, не думайте об этом как (Type*)* ! Грамматика так не работает. Подумайте об этом как Type *(*) , или, скорее Type (*(*name)) : выражение (*(*name)) имеет тип Type . (Но это не работает для ссылок или функций.)