C ++ - char ** argv vs. char * argv []

В чем разница между char** argv и char* argv[]? в int main(int argc, char** argv) и int main(int argc, char* argv[])?

Они одинаковы? Тем более что в первой части нет [].


person Simplicity    schedule 04.03.2011    source источник


Ответы (7)


Они полностью эквивалентны. char *argv[] должен читаться как массив указателей на char, а аргумент массива понижается до указателя, поэтому указатель на указатель на char или char **.

То же самое и в C.

person Fred Foo    schedule 04.03.2011
comment
Спасибо за ответ. Не могли бы вы объяснить это поподробнее: ...and an array argument is demoted to a pointer, so pointer to pointer to char, or char **. - person Simplicity; 04.03.2011
comment
@user: Это языковое правило. Когда вы объявляете или определяете функцию с синтаксисом X foo(Y a[]), она фактически становится X foo(Y *a). То, что выглядит как аргумент массива функции, на самом деле является указателем. Поскольку argv объявлен как массив (указателей), он становится указателем (на указатели). - person Fred Foo; 04.03.2011
comment
Часть указателя на char свободна для char** argv. Но откуда берется первый указатель? - person Simplicity; 04.03.2011
comment
@user: давайте сделаем typedef char *String, поэтому String - это псевдоним для указателя на char. Теперь argv можно объявить String argv[], который представляет собой массив String. Поскольку это аргумент, это действительно указатель на String, поэтому String *argv. Надеюсь, это проясняет, я действительно не понимаю ваш вопрос. - person Fred Foo; 04.03.2011
comment
Итак ... если я правильно понимаю, можно ли сказать ... 1. У нас есть указатель на массив (* argv, который совпадает с argv []) 2. Внутри этого массива у нас есть несколько указателей на символы - person Allen; 27.08.2017

Они действительно точно такие же.

Золотое правило массивов, которое следует запомнить:

«Имя массива - указатель на первый элемент массива».

Итак, если вы заявите следующее:

char text[] = "A string of characters.";

Тогда переменная text является указателем на первый символ в массиве символов, который вы только что объявили. Другими словами, «текст» имеет тип char *. Когда вы обращаетесь к элементу массива с помощью [index], на самом деле вы добавляете смещение index к указателю на первый элемент массива, а затем разыменование этого нового указателя. Поэтому следующие две строки инициализируют обе переменные значением 't':

char thirdChar = text[3];
char thirdChar2 = *(text+3);

Использование квадратных скобок - это удобство, обеспечиваемое языком, которое делает код более читабельным. Но то, как это работает, очень важно, когда вы начинаете думать о более сложных вещах, таких как указатели на указатели. char** argv то же самое, что и char* argv[], потому что во втором случае «имя массива является указателем на первый элемент в массиве».

Из этого вы также сможете понять, почему индексы массива начинаются с 0. Указатель на первый элемент - это имя переменной массива (снова золотое правило) плюс смещение ... ничего!

Я спорил с моим другом о том, что лучше использовать здесь. С обозначением char* argv[] читателю может быть яснее, что это на самом деле «массив указателей на символы», в отличие от обозначения char** argv, которое можно читать как «указатель на указатель на символ». Я считаю, что последнее обозначение не передает столько информации читателю.

Приятно знать, что они точно такие же, но для удобства чтения я думаю, что если намерение - это массив указателей, то нотация char* argv[] передает это намного яснее.

person James Bedford    schedule 04.03.2011
comment
Спасибо за ответ. Не могли бы вы просто сказать мне, в чем разница между этими двумя формами? - person Simplicity; 04.03.2011
comment
Я отредактировал свой ответ, который должен дать более подробное объяснение. - person James Bedford; 04.03.2011
comment
Золотое правило массивов, которое следует запомнить, абсолютно не то, что вы написали. Это ужасный, разочаровывающий миф, который просто не умрет. Имя массива может распадаться на указатель на первый элемент этого массива. Это абсолютно не то же самое, что эквивалентность или идентичность. - person Lightness Races in Orbit; 06.05.2016
comment
Что на самом деле здесь происходит, так это то, что распад неявно присутствует в списках параметров функции. - person Lightness Races in Orbit; 06.05.2016
comment
Понятия не имею, о чем вы говорите. : S - person James Bedford; 06.05.2016
comment
@JamesBedford: Он говорит, что имя массива НЕ является указателем на первый элемент. Он распадается на такой указатель в определенных контекстах, таких как вызовы функций, что и происходит здесь. - person Nick Matteo; 06.05.2016
comment
Различие между временем компиляции и временем выполнения? Я нашел это полезным. stackoverflow.com/questions/1461432/what-is-array-decaying < / а> - person James Bedford; 06.05.2016
comment
@JamesBedford: Нет, на самом деле это не время компиляции и время выполнения. Массив типа int x[10] - это стековая переменная, состоящая из 10 целых чисел. (Когда его охватывающая область видимости помещается в стек, его фрейм включает место для этих 10 целых чисел.) Это не указатель, который может быть переменной, содержащей адрес чего-либо. Когда вы создаете массив, в стеке не хранятся адреса, в отличие от того, когда вы создаете указатель. Однако, когда вы передаете массив функции, он распадается; функция получает указатель, а не массив. - person Nick Matteo; 07.05.2016
comment
Подумайте: когда вы берете адрес массива, &x, вы получаете адрес данных в массиве. Если бы x был указателем, вы бы вместо этого получили адрес ячейки памяти, содержащей указатель на данные, но это не так. - person Nick Matteo; 07.05.2016
comment
&x - адрес в стеке. Рассмотрим sizeof(x) и sizeof(&x). - person raylu; 18.06.2016

Для всех практических целей они одинаковы. Это связано с обработкой C / C ++ массивов, переданных в качестве аргументов, где массив распадается на указатель.

person Erik    schedule 04.03.2011

По первой части вопроса:

  • char ** argv: указатель на указатель на char
  • char * argv []: указатель на массив

Итак, вопрос в том, являются ли указатель на тип C и массив C [] одним и тем же. Это совсем не так, НО они эквивалент при использовании в подписях.

Другими словами, в вашем примере нет никакой разницы, но важно помнить о различии между указателем и массивом в противном случае.

person David Cournapeau    schedule 04.03.2011
comment
Ссылка на C FAQ, но вопрос помечен как C ++. - person Fred Foo; 04.03.2011
comment
C ++ - это расширение C ... Итак, все, что относится к C, имеет отношение к C ++. - person James Bedford; 04.03.2011
comment
Неа. Есть мысли, что незаконны в C и разрешены в C ++, поэтому, когда какой-то источник говорит, что это незаконно ... не означает, что это относится к C ++. - person Mephane; 04.03.2011
comment
Однако в этом аспекте C и C ++ очень похожи. - person David Cournapeau; 05.03.2011
comment
char * argv []: указатель на массив Нет. T* param[] переписывается на T** param на уровне синтаксиса. Вы можете передать T** в эту функцию, которая не имеет ничего общего с массивом. - person Lightness Races in Orbit; 06.05.2016

Форма скобок полезна только в объявлениях операторов, таких как:

char *a[] = {"foo", "bar", "baz"};
printf("%d\n", sizeof a / sizeof *a);
// prints 3

Потому что во время компиляции он знает размер массива. Когда вы передаете форму скобок в качестве параметра функции (основной или какой-либо другой), компилятор не знает, каким будет размер массива во время выполнения, поэтому он точно такой же, как char ** a. Я предпочитаю char ** argv, поскольку очевидно, что sizeof не будет работать, как в форме объявления оператора.

person Jake    schedule 29.10.2013

Есть разница между TYPE * NAME и TYPE NAME[] как в C, так и в C ++. В C ++ оба типа не взаимозаменяемы. Например, следующая функция недопустима (вы получите сообщение об ошибке) в C ++, но допустима в C (вы получите предупреждение):

int some (int *a[3]) // a is array of dimension 3 of pointers to int
{
    return sizeof a;
}

int main ()
{
    int x[3][3];
    std::cout << some(x)<< std::endl;
    return 0;
}

Чтобы сделать его законным, просто измените подпись на int some (int (*a)[3]) (указатель на массив из 3 целых чисел) или int some (int a[][3]). Число в последних квадратных скобках должно быть равно аргументу. Преобразование из массива массивов в массив указателей недопустимо. Преобразование указателя в указатель на массив массивов также недопустимо. Но преобразование указателя в указатель на массив указателей допустимо!

Так что помните: Не имеет значения только подпись ближайшего к разыменованию типа, другие имеют значение (конечно, в контексте указателей и массивов).

Предположим, у нас есть указатель as на указатель на int:

int ** a;
&a     ->     a    ->    *a    ->    **a
(1)          (2)         (3)          (4)
  1. Вы не можете изменить это значение, тип - int ***. Может приниматься функцией как int **b[] или int ***b. Лучшее - int *** const b.
  2. Тип int **. Может приниматься функцией как int *b[] или int ** b. Скобки массива declaratin можно оставить пустыми или содержать любое число.
  3. Тип int *. Может приниматься функцией как int b[], int * b или даже void * b
  4. Следует принимать как параметр типа int. Я не хочу вдаваться в подробности, вроде неявного вызова конструктора.

Отвечая на ваш вопрос: настоящий тип аргументов в основной функции - это char ** argv, поэтому его можно легко представить как char *argv[] (но не как char (*argv)[]). Также argv имя основной функции может быть безопасно изменилось. Вы можете легко это проверить: std::cout << typeid(argv).name(); (PPc = указатель на стр. На символ)

Кстати: есть классная возможность передавать массивы как ссылки:

void somef(int (&arr)[3])
{
    printf("%i", (sizeof arr)/(sizeof(int))); // will print 3!
}

Более того указатель на что угодно может быть неявно принят (преобразован) функцией как недействительный указатель. Но только один указатель (не указатель на указатель и т. Д.).

Дальнейшее чтение:

  1. Бьярн Страуструп, C ++, глава 7.4
  2. Часто задаваемые вопросы по указателям C
person yanpas    schedule 06.05.2016
comment
Существует разница между TYPE * NAME и TYPE NAME [] как в C, так и в C ++. Вздор. В C ++ оба типа не взаимозаменяемы. Нет, на самом деле существует только первый тип. Например, следующая функция недопустима (вы получите сообщение об ошибке) Это потому, что int* ar[3] и int* ar[] - совершенно разные вещи. Дезинформация продолжается и продолжается ... - person Lightness Races in Orbit; 06.05.2016
comment
Что значит clothest ?? - person Dan Bechard; 02.08.2016
comment
@ Дэн, я имел в виду ближайший, извини - person yanpas; 02.08.2016
comment
@yanpas Вы можете отредактировать свой ответ, чтобы исправить его, если хотите. - person Dan Bechard; 02.08.2016

Оба варианта одинаковы для вашего использования, за исключением следующих незначительных различий:

  • Sizeof даст разные результаты для обоих
  • Также второй не может быть переназначен новой области памяти, так как это массив
  • Со вторым вы можете использовать только те индексы, которые действительны. Это не определено C / C ++, если вы попытаетесь использовать индекс массива, превышающий длину массива. Однако с char ** вы можете использовать любой индекс от 0 до ...
  • Вторая форма может использоваться только как формальные параметры функции. Хотя first может даже использоваться для объявления переменных в стеке.
person user5858    schedule 04.03.2011
comment
Какие? В контексте, который запрашивает OP, все они неверны. - person Anton Golov; 27.06.2012