Должен ли я использовать char ** argv или char * argv [] в C? - PullRequest
120 голосов
/ 23 апреля 2009

Я только изучаю C, и мне было интересно, какой из них мне следует использовать в моем основном методе. Есть ли разница?

Редактировать: Так какой из них более распространен?

Ответы [ 10 ]

155 голосов
/ 23 апреля 2009

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

В области параметров и массивов есть несколько запутанных правил, которые должны быть ясны перед тем, как продолжить. Во-первых, то, что вы объявляете в списке параметров, обрабатывается особым образом. Существуют такие ситуации, когда вещи не имеют смысла как параметр функции в C. Это

  • Функции в качестве параметров
  • Массивы в качестве параметров

Массивы в качестве параметров

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

В C по этим причинам значения массива не существуют. Если вы хотите получить значение массива, вместо этого вы получите указатель на первый элемент этого массива. И в этом на самом деле уже лежит решение. Вместо того, чтобы рисовать неверный параметр массива, компилятор C преобразует тип соответствующего параметра в указатель. Запомните это, это очень важно. Параметр не будет массивом, но будет указателем на соответствующий тип элемента.

Теперь, если вы попытаетесь передать массив, то вместо этого передается указатель на первый элемент массива.

Экскурсия: Функции в качестве параметров

Для завершения, и поскольку я думаю, что это поможет вам лучше понять суть вопроса, давайте посмотрим, как обстоят дела, когда вы пытаетесь использовать функцию в качестве параметра. Действительно, во-первых, это не имеет никакого смысла. Как параметр может быть функцией? Да, мы хотим переменную в этом месте, конечно! То, что делает компилятор, когда это происходит, опять же преобразует функцию в указатель на функцию . Попытка передать функцию передаст указатель на соответствующую функцию. Итак, следующие значения одинаковы (аналогично примеру с массивом):

void f(void g(void));
void f(void (*g)(void));

Обратите внимание, что скобки вокруг *g необходимы. В противном случае он указывал бы функцию, возвращающую void*, вместо указателя на функцию, возвращающую void.

Вернуться к массивам

Теперь я сказал в начале, что массивы могут иметь неполный тип - что происходит, если вы еще не указали размер. Поскольку мы уже выяснили, что параметр массива не существует, но вместо этого любой параметр массива является указателем, размер массива не имеет значения. Это означает, что компилятор переведет все следующее, и все это одно и то же:

int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);

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

// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory. 
int main(int c, char *argv[static 5]);

// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);

// says the same as the previous one
int main(int c, char ** const argv);

Последние две строки говорят о том, что вы не сможете изменить «argv» внутри функции - она ​​стала указателем const. Только немногие компиляторы C поддерживают эти функции C99. Но эти особенности дают понять, что «массив» на самом деле не один. Это указатель.

Слово предупреждения

Обратите внимание, что все, что я сказал выше, верно только в том случае, если вы получили массив в качестве параметра функции. Если вы работаете с локальными массивами, массив не будет указателем. Он будет вести себя как указатель, потому что, как объяснялось ранее, массив будет преобразован в указатель при чтении его значения. Но это не следует путать с указателями.

Один классический пример:

char c[10]; 
char **c = &c; // does not work.

typedef char array[10];
array *pc = &c; // *does* work.

// same without typedef. Parens needed, because [...] has 
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;
12 голосов
/ 23 апреля 2009

Вы можете использовать любой из них, это зависит от того, как вы хотите его использовать. char* argv[] (в основном) эквивалентно char ** argv. Обе формы являются указателями на указатели на char, единственное отличие состоит в том, что с char *argv[] вы сообщаете компилятору, что значение argv не изменится (хотя значения, на которые он указывает, все еще могут). На самом деле, я ' Я не прав, они полностью эквивалентны. См. litb комментарии и его ответ .

Это действительно зависит от того, как вы хотите его использовать (и вы можете использовать любой из них):

// echo-with-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char **argv)
{
  while (--argc > 0)
  {
    printf("%s ", *++argv);
  }
  printf("\n");
  return 0;
}

// echo-without-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char *argv[])
{
  int i;
  for (i=1; i<argc; i++)
  {
    printf("%s ", argv[i]);
  }
  printf("\n");
  return 0;
}

Что касается того, что более распространено - это не имеет значения. Любой опытный программист на Си, читающий ваш код, увидит, что оба являются взаимозаменяемыми (при правильных условиях). Так же, как опытный говорящий по-английски читает «они» и «они» одинаково легко.

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

9 голосов
/ 23 апреля 2009

Вы можете использовать любую из двух форм, так как в C массивы и указатели взаимозаменяемы в списках параметров функции. Смотри http://en.wikipedia.org/wiki/C_(programming_language)#Array-pointer_interchangeability.

8 голосов
/ 23 апреля 2009

Это не имеет значения, но я использую char *argv[], потому что это показывает, что это массив фиксированной длины строк переменной длины (которые обычно char *).

4 голосов
/ 23 апреля 2009

На самом деле это не имеет значения, но последний более читабелен. Вам дают массив указателей на символы, как говорит вторая версия. Однако он может быть неявно преобразован в указатель с двумя символами, как в первой версии.

1 голос
/ 08 февраля 2015

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

1 голос
/ 23 апреля 2009

char ** → указатель на символьный указатель, а char * argv [] означает массив символьных указателей. Поскольку мы можем использовать указатель вместо массива, можно использовать оба.

0 голосов
/ 03 апреля 2012

Я знаю, что это устарело, но если вы только изучаете язык программирования C и не делаете с ним ничего особенного, не используйте параметры командной строки.

Если вы не используете аргументы командной строки, не используйте также. Просто объявите основную функцию как int main() Если вы

  • Хотите, чтобы пользователь вашей программы мог перетаскивать файл в вашу программу, чтобы вы могли изменить результат вашей программы вместе с ним или
  • Хотите обработать параметры командной строки (-help, /? или любые другие, которые идут после program name в терминале или командной строке)

используйте то, что имеет больше смысла для вас. В противном случае просто используйте int main() В конце концов, если вы захотите добавить параметры командной строки, вы можете легко отредактировать их позже.

0 голосов
/ 23 апреля 2009

Я не вижу особой пользы в использовании одного из подходов вместо другого - используйте соглашение, наиболее соответствующее остальному вашему коду.

0 голосов
/ 23 апреля 2009

Если вам нужно переменное или динамическое количество строк, с символом ** будет проще работать. Если число строк фиксировано, то будет предпочтительнее char * var [].

...