Как указано в вопросе OP, в стандарте C11 прямо указано, что переменные argc
и argv
, а также строки, на которые указывает массив argv
, могут быть изменены. Вопрос в том, поддаются ли эти указатели изменению или нет. Стандарт, похоже, не указывает это явно так или иначе.
Следует отметить два ключевых момента, касающихся формулировок в стандарте:
Если бы указатели предполагались неизменяемыми, стандарт мог бы прояснить это, потребовав, чтобы main объявлялся как int main(int argc, char *const argv[])
, как упоминалось в хаках в другом ответе на этот вопрос.
Тот факт, что нигде в стандарте const
не упоминается в связи с argv
, кажется преднамеренным. То есть отсутствие const
выглядит не опционально, а продиктовано стандартом.
Стандарт последовательно называет argv
массивом. Изменение массива относится к изменению его элементов. Таким образом, кажется очевидным, что формулировка в стандарте относится к изменению элементов в массиве argv
, когда говорится, что argv
можно изменить.
С другой стороны, массив параметров в языке C (на основе проекта C11 N1570, §6.7.6.3p7) "должен быть изменен на "уточненный указатель на тип"". Таким образом, следующий код,
int foo(int x[2], int y[2])
{
if (x[0] > y[0])
x = y;
return x[1];
}
действителен C11, так как x
и y
изменены на int *x
и int *y
соответственно. (Это также повторяется в проекте C11 N1570, §6.3.2.1p3: "... массив... преобразуется в выражение с типом "указатель на тип", который указывает на начальный элемент массива... .".) Очевидно, того же не было бы, если бы x
и y
были объявлены как локальные или глобальные массивы, а не параметры функции.
Что касается языковой юриспруденции, я бы сказал, что стандарт так или иначе не утверждает этого, хотя он подразумевает, что указатели также должны быть модифицируемыми. Таким образом, как ответ на ОП: оба.
На практике существует очень давняя традиция изменять указатели в массиве argv
. Многие библиотеки имеют функции инициализации, которые принимают указатель на argc
и указатель на массив argv
, а некоторые из них изменяют указатели в массиве argv
(удаляя параметры, характерные для библиотеки); например, GTK+ gtk_init()
и MPI_Init()
(хотя, по крайней мере, OpenMPI прямо заявляет, что не проверяет или изменить их). Найдите объявление параметра (int *argc, char ***argv)
; единственная причина для этого - предполагая, что намерение состоит в том, чтобы вызываться из main()
с использованием (&argc, &argv)
- состоит в том, чтобы изменить указатели, проанализировать и удалить параметры командной строки, специфичные для библиотеки, из параметров командной строки, изменив как argc
, так и указатели в argv
по мере необходимости.
(Первоначально я заявил, что средство getopt()
в POSIX полагается о том, что указатели могут быть изменены — эта функция восходит к 1980 году, принята большинством разновидностей Unix и стандартизирована в POSIX.2 в 1997 году — но это неверно, как указал Джонатан Леффлер в комментарии: POSIX getopt()
не изменяет фактические указатели; только GNU getopt()
изменяет, и это только тогда, когда переменная окружения POSIXLY_CORRECT
не установлена. И GNU getopt_long()
, и BSD getopt_long()
изменяют указатели, если не установлено POSIXLY_CORRECT
, но они намного моложе и менее распространены по сравнению с getopt()
.)
В мире Unix считалось "переносимым" изменение содержимого строк, на которые указывает массив argv[]
, и отображение измененных строк в списке процессов. Одним из примеров того, как это было полезно, является пакет DJB daemontools, readproctitle. (Обратите внимание, что строки должны быть изменены на месте и не могут быть расширены, чтобы изменения были видны в списке процессов.)
Все это указывает на очень давнюю традицию, фактически почти с момента рождения С как языка программирования и определенно предшествующую стандартизации С, обработки argc
, argv
, указателей в массиве argv
и содержимого строк, на которые указывает эти указатели, как изменяемые.
Поскольку цель стандарта C состоит не в том, чтобы определить новое поведение, а в том, чтобы систематизировать существующее поведение в различных реализациях (для обеспечения переносимости, надежности и т. д.), можно с уверенностью предположить, что это было непреднамеренным упущением со стороны разработчиков стандарта. явно указать указатели в массиве argv
как изменяемые. Все остальное нарушило бы традицию и явно противоречило бы стандарту POSIX (который также предназначен для обеспечения переносимости между системами и расширяет возможности C, не включенные в стандарт ISO C).
person
Nominal Animal
schedule
31.01.2016
argv
можно изменить, аargv
содержит указатели, о которых вы говорите, да. - person Simon Shine   schedule 30.01.2016argv
можно изменить, не означает ли это, что можно сделать толькоargv = ...
, а не обязательно*argv = ...
? - person cadaniluk   schedule 30.01.2016main
являетсяint main(int argc, char *argv[])
, кажется очевидным, что параметрargv
представляет собой массив изchar *
указателей, а неchar const *argv[]
указателей. Таким образом, строки должны быть модифицируемыми. - person nsilent22   schedule 30.01.2016char* str = "foo";
, а*str = 'c';
- это неопределенное поведение. Так что я бы оставил здесьconst
в стороне. - person cadaniluk   schedule 30.01.2016argv
— это локальная переменная, поэтому ее все равно можно изменить. Но вопрос касается*argv
и других указателей. - person too honest for this site   schedule 30.01.2016*argv
. Это может быть полезно для автономной среды, но для этого стандарт не определяет поведение/функции при запуске, поэтому в любом случае это зависит от вашей реализации и среды. - person too honest for this site   schedule 30.01.2016int main(void)
иint main(int, char**)
в размещенной реализации? - person cadaniluk   schedule 30.01.2016char *str = "foo"
, вы указываетеstr
на строкуchar const *
, о чем вас предупредят современные компиляторы. - person nsilent22   schedule 30.01.2016str
делает то же самое, поэтому мой контраргумент. Речь идет о четко определенном и неопределенном, а не о какой-то диагностике, даже не указанной в стандарте C11, насколько мне известно. Кроме того, тип строкового литерала —char[N]
, если я еще в порядке. - person cadaniluk   schedule 30.01.2016main
и несет ответственность за совместимость. В любом случае, принятие этого объявления как данного означает, что*argv
на самом деле можно изменить. - person too honest for this site   schedule 30.01.2016char *str = "string";
даст вам предупреждение. Конечно, "обычный" программист мог бы это сделать, но компилятор/библиотека должны соответствовать некоторым стандартам и не передавать в основную функцию аргументы неправильного типа. - person nsilent22   schedule 30.01.2016*argv
также UB. (Обратите внимание, что в стандарте не указано, что строковый литерал — этоconst char []
, а только то, что запись в него — это UB. Что делает литерал только техническиconst char []
). - person too honest for this site   schedule 30.01.2016