Подтвердить что ты не робот

Является ли 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, поэтому в отсутствие конкретной формулировки наоборот мы должны предположить, что они являются изменяемыми объектами.
4b9b3361

Ответ 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 из предложения и двусмысленность исчезает.