В чем разница между char** argv
и char* argv[]
? в int main(int argc, char** argv)
и int main(int argc, char* argv[])
?
Они одинаковы? Тем более что в первой части нет []
.
В чем разница между char** argv
и char* argv[]
? в int main(int argc, char** argv)
и int main(int argc, char* argv[])
?
Они одинаковы? Тем более что в первой части нет []
.
Они полностью эквивалентны. char *argv[]
должен читаться как массив указателей на char
, а аргумент массива понижается до указателя, поэтому указатель на указатель на char
или char **
.
То же самое и в C.
...and an array argument is demoted to a pointer, so pointer to pointer to char, or char **.
- person Simplicity; 04.03.2011
X foo(Y a[])
, она фактически становится X foo(Y *a)
. То, что выглядит как аргумент массива функции, на самом деле является указателем. Поскольку argv
объявлен как массив (указателей), он становится указателем (на указатели).
- person Fred Foo; 04.03.2011
char** argv
. Но откуда берется первый указатель?
- person Simplicity; 04.03.2011
typedef char *String
, поэтому String
- это псевдоним для указателя на char
. Теперь argv
можно объявить String argv[]
, который представляет собой массив String
. Поскольку это аргумент, это действительно указатель на String
, поэтому String *argv
. Надеюсь, это проясняет, я действительно не понимаю ваш вопрос.
- person Fred Foo; 04.03.2011
Они действительно точно такие же.
Золотое правило массивов, которое следует запомнить:
«Имя массива - указатель на первый элемент массива».
Итак, если вы заявите следующее:
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[]
передает это намного яснее.
int x[10]
- это стековая переменная, состоящая из 10 целых чисел. (Когда его охватывающая область видимости помещается в стек, его фрейм включает место для этих 10 целых чисел.) Это не указатель, который может быть переменной, содержащей адрес чего-либо. Когда вы создаете массив, в стеке не хранятся адреса, в отличие от того, когда вы создаете указатель. Однако, когда вы передаете массив функции, он распадается; функция получает указатель, а не массив.
- person Nick Matteo; 07.05.2016
&x
, вы получаете адрес данных в массиве. Если бы x
был указателем, вы бы вместо этого получили адрес ячейки памяти, содержащей указатель на данные, но это не так.
- person Nick Matteo; 07.05.2016
&x
- адрес в стеке. Рассмотрим sizeof(x)
и sizeof(&x)
.
- person raylu; 18.06.2016
Для всех практических целей они одинаковы. Это связано с обработкой C / C ++ массивов, переданных в качестве аргументов, где массив распадается на указатель.
По первой части вопроса:
Итак, вопрос в том, являются ли указатель на тип C и массив C [] одним и тем же. Это совсем не так, НО они эквивалент при использовании в подписях.
Другими словами, в вашем примере нет никакой разницы, но важно помнить о различии между указателем и массивом в противном случае.
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 не будет работать, как в форме объявления оператора.
Есть разница между 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)
int ***
. Может приниматься функцией как int **b[]
или int ***b
. Лучшее - int *** const b
.int **
. Может приниматься функцией как int *b[]
или int ** b
. Скобки массива declaratin можно оставить пустыми или содержать любое число.int *
. Может приниматься функцией как int b[]
, int * b
или даже void * b
Отвечая на ваш вопрос: настоящий тип аргументов в основной функции - это 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!
}
Более того указатель на что угодно может быть неявно принят (преобразован) функцией как недействительный указатель. Но только один указатель (не указатель на указатель и т. Д.).
Дальнейшее чтение:
int* ar[3]
и int* ar[]
- совершенно разные вещи. Дезинформация продолжается и продолжается ...
- person Lightness Races in Orbit; 06.05.2016
clothest
??
- person Dan Bechard; 02.08.2016
Оба варианта одинаковы для вашего использования, за исключением следующих незначительных различий: