Является ли argv [n] доступным для записи?
C11 5.1.2.2.1/2 говорит:
Параметры argc
и argv
, а строки, на которые указывает массив argv
, должны могут быть модифицированы программой и сохраняют свои последние сохраненные значения между программой запуск и завершение программы.
Моя интерпретация этого заключается в том, что она указывает:
int main(int argc, char **argv)
{
if ( argv[0][0] )
argv[0][0] = 'x'; // OK
char *q;
argv = &q; // OK
}
однако он ничего не говорит о:
int main(int argc, char **argv)
{
char buf[20];
argv[0] = buf;
}
Разрешен ли argv[0] = buf;
?
Я вижу (по крайней мере) два возможных аргумента:
- Вышеприведенная цитата преднамеренно упомянула
argv
и argv[x][y]
, но не argv[x]
, поэтому было намерено, что она не модифицируется
-
argv
- это указатель на объекты не const
, поэтому в отсутствие конкретной формулировки наоборот мы должны предположить, что они являются изменяемыми объектами.
Ответы
Ответ 1
IMO, код типа argv[1] = "123";
- UB.
"Параметры argc и argv и строки, на которые указывает массив argv, должны могут быть модифицированы программой и сохраняют свои последние сохраненные значения между программой запуск и завершение программы". C11dr §5.1.2.2.1 2
Напомним, что const
вошел в C через много лет после создания C.
Похоже, что char *s = "abc";
допустимо, когда оно должно быть const char *s = "abc";
. Потребность в const
не требовалась, иначе существующий код был бы сломан с введением const
.
Аналогично, даже если argv
сегодня следует считать char * const argv[]
или какой-либо другой сигнатурой с const
, отсутствие const
в char *argv[]
не заканчивается, укажите потребности const
-ness argv
, argv[]
или argv[][]
. Потребности const
нуждаются в помощи спецификации.
Из моего чтения, так как спецификация молчала о проблеме, это UB.
Undefined поведение в противном случае указано в этом Международном стандарте словами "undefined" или "сильным" отсутствием какого-либо явного определения поведения "§4 2
[edit]:
main()
- очень специальная функция C. То, что допустимо в других функциях, разрешено или не разрешено в main()
. Спецификация C указывает атрибуты о своих параметрах, которые дали подпись int argc, char *argv[]
, которая не нужна. main()
, в отличие от других функций в C, может иметь альтернативную подпись int main(void)
и потенциально другие. main()
не является реентерабельным. Поскольку спецификатор C не подходит для подробного описания того, что можно изменить: argc
, argv
, argv[][]
, разумно поставить вопрос, может ли argv[]
быть модифицируемым из-за его отсутствия из спецификации, утверждающей, что код может,
Учитывая специальность main()
и отсутствие указания на то, что argv[]
можно модифицировать, консервативный программист рассматривал бы эту серость как UB, ожидая дальнейшего уточнения спецификации C.
Если argv[i]
модифицируется на данной платформе, то, конечно, диапазон i
не должен превышать argc-1
.
Как "argv[argc]
должен быть нулевой указатель", назначение argv[argc]
чему-то, кроме NULL
, является нарушением.
Хотя строки могут быть изменены, код не должен превышать исходную длину строки.
char *newstr = "abc";
if (strlen(newstr) <= strlen(argv[1]))
strcpy(argv[1], newstr);
Ответ 2
argc
является просто a int
и может быть изменен без каких-либо ограничений.
argv
является модифицируемым char **
. Это означает, что argv[i] = x
действителен. Но он ничего не говорит о том, что argv[i]
сам модифицируется. Таким образом, argv[i][j] = c
приводит к поведению undefined.
Функция getopt
стандартной библиотеки C модифицирует argc и argv, но никогда не изменяет фактические массивы char.
Ответ 3
Ясно, что argv
и argv[x][x]
являются модифицируемыми. Если argv
является изменяемым, то он может указывать на другой первый элемент массива char
и, следовательно, argv[x]
может указывать на первый элемент некоторой другой строки. В конечном счете argv[x]
тоже может быть изменен, и это может быть причиной того, что нет необходимости упоминать его явно в стандарте.
Ответ 4
Ответ заключается в том, что argv - это массив и да, его содержимое может быть изменено.
Ключ ранее в том же разделе:
Если значение argc больше нуля, члены массив argv [0] через argv [argc-1] включительно должен содержать указатели на строки, которые указаны определяемые реализацией значения среды хоста перед запуском программы.
Отсюда видно, что argv следует рассматривать как массив определенной длины (argc). Тогда * argv является указателем на этот массив, разлагающимся на указатель.
Прочитайте в этом контексте утверждение о том, что "argv должно быть модифицируемым... и сохранить его содержимое" явно предполагает, что содержимое этого массива может быть модифицируемым.
Я признаю, что в формулировке сохраняется определенная двусмысленность, особенно в отношении того, что может произойти, если argc будет изменен.
Чтобы быть ясным, я говорю, что я читаю этот язык как значение:
[содержимое] argv [array] и строки, на которые указывает массив argv, должны быть модифицируемы...
Таким образом, оба указателя в массиве и строки, на которые они указывают, находятся в памяти чтения и записи, никакого вреда не происходит, изменяя их, и оба сохраняют свои значения для жизни программы. Я бы ожидал, что это поведение можно найти во всех основных реализациях библиотек исполняемых файлов C/С++ без исключения. Это не UB.
Неоднозначность - это упоминание argc. Трудно представить себе какую-либо цель или любую реализацию, в которой значение argc (которое, как представляется, является просто локальным параметром функции) не может быть изменено, так зачем его упоминать? В стандарте четко указывается, что функция может изменять значение своих параметров, поэтому зачем обращаться с арцем специально в этом отношении? Именно это неожиданное упоминание о argc вызвало эту озабоченность по поводу argv, которая в противном случае проходила бы без замечаний. Удалить argc из предложения и двусмысленность исчезает.