Должен ли я использовать char ** argv или char * argv [] в C?

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

Изменить:. Какой из них наиболее распространен?

Ответ 1

Поскольку вы просто изучаете 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;

Ответ 2

Вы можете использовать либо, это зависит от того, как вы хотите его использовать. 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;
}

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

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

Ответ 4

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

Ответ 5

Это не имеет особого значения, но последнее более читаемо. То, что вам дано, представляет собой массив указателей char, как говорит вторая версия. Он может быть неявно преобразован в двойной указатель char, как в первой версии.

Ответ 6

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

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

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

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