Как обрабатывать назначения массива символов argv?

Я нашел два способа передачи аргументов командной строки в массив символов:

int main (int argc, char **argv)
{
  const char *s1 = argv[0];
  char s2[256];
  strcpy(s2, argv[0]);

  printf("s1: %s\ns2: %s\n\n", s1, s2);
}

Скомпилировано с помощью компилятора IBM xlc в системе AIX.

[Моя подсказка]> ./a.out

s1: ./a.out

s2: ./a.out

Какая реализация (s1 или s2) правильная? s1 хорош, потому что argv[0] может быть любой длины. s2 требует, чтобы длина argv[0] не превышала 256 символов.

Я не понимаю, как/почему должен работать s1. Я думаю, что правая часть s1 должна требоваться во время компиляции, но я думаю, что она генерируется во время выполнения.


person Pete    schedule 31.07.2009    source источник


Ответы (6)


Причина, по которой s1 работает, заключается в том, что тип argv[0] является указателем. Вы просто назначаете адрес (а не фактическое значение), что безопасно. Вы не выполняете какое-либо распределение или приведение.

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

person Jordan Parmer    schedule 31.07.2009
comment
Если это так, не должен ли я изменить аргументы int main на (const int argc, const char **argv)? - person Pete; 31.07.2009
comment
Единственная причина, по которой вы не можете этого сделать, - это переносимость: возможно, существуют компиляторы, которые этого не примут. - person quark; 31.07.2009
comment
const — это новое дополнение к языку C, и, поскольку функция main() была определена еще в далеком детстве C, она не имеет оформления const. Однако char* можно преобразовать в const char*, поэтому вам не нужно беспокоиться о несогласованности. - person Kim Gräsman; 31.07.2009
comment
j0rd4n: Это не совсем так. Вы всегда можете указать указатель на константу на указатель на неконстанту. Наоборот, это не законно. - person quark; 31.07.2009
comment
Подпись main приходит к нам из туманных глубин времени, и с ней нельзя шутить. - person dmckee --- ex-moderator kitten; 31.07.2009

Если вы не хотите менять строку, тогда s1 будет работать.

Если вы хотите изменить строку, вы можете сделать ее копию. Вы должны использовать более безопасные методы strnlen() и strncpy(), если ваша система их поддерживает.

person Karl Voigtland    schedule 31.07.2009

Я думаю, что правая часть s1 должна требоваться во время компиляции, но я думаю, что она генерируется во время выполнения.

Нет, это требуется каждый раз, когда встречается оператор. Например:

void f() {
   int x = 1;
   ...
}

Целое число x будет инициализироваться равным 1 каждый раз при вызове функции, а не во время компиляции.

person Community    schedule 31.07.2009

s2 обладает восхитительным свойством быть восприимчивым к переполнению буфера.

Я видел, как люди меняли значение argv[0]. В некоторых случаях (в некоторых ОС) изменение argv[0] заставит программу отображаться в ps как то, на что вы ее изменили.

person user47559    schedule 31.07.2009

Если вы просто хотите сослаться на аргумент, не внося в него никаких изменений, тогда s1 будет правильным.

Если вам нужно каким-либо образом изменить аргумент, вам нужно сделать копию это похоже на пример s2, но в примере s2 вам нужно явно проверить, не превышает ли длина буфера, в который вы его копируете. Например, если вы принимаете такой аргумент, как имя_файла.jpg в качестве входных данных, и сохраняете файл имя_файла.gif в качестве вывода, вам необходимо сделать копию этого аргумента, поскольку вы будете менять расширение с .jpg на .gif.

person KPexEA    schedule 31.07.2009

Я бы выбрал s1, особенно для argv[n], где n > 0. Такие вещи, как s2, открывают вас для классических атак переполнения буфера. По сути, пользователь может отформатировать аргумент длиной более 256 символов и перезаписать информацию в стеке, чтобы он мог запускать любой код, который он хочет.

person Dan Hook    schedule 31.07.2009