Почему argc не является константой?


104
int main( const int argc , const char[] const argv)

Поскольку в пункте № 3 эффективного C ++ сказано: «По возможности используйте const», я начинаю думать: «Почему бы не сделать эти« постоянные »параметры const?».

Есть ли какой-нибудь сценарий, при котором значение argcизменяется в программе?


38
Вы можете объявить argcкак const.
Оливер Чарльзуорт

14
Я видел много производственного кода качества, который делает это:--argc
Аникет Инге

2
Я думаю, это связано с наследием C, но не знаю, как это сделать.
Луис Мачука

13
Я подозреваю, что «по возможности используйте константу» относится к вещам, переданным по ссылке, где любые изменения внутри функции сохраняются после выхода из функции. Это неверно с вещами, переданными по значению, например, с целым числом, поэтому ничего не получится, создавая это const; действительно, передача argcкак const intсредство, которое вы не можете затем использовать argc, скажем, как счетчик внутри функции.
Стив Мельникофф

4
@SteveMelnikoff: Я не согласен с тем, что от constиспользования параметра передачи по значению ничего не получится . См., Например, stackoverflow.com/a/8714278/277304 и stackoverflow.com/a/117557/277304
leonbloy

Ответы:


114

В этом случае история играет важную роль. C определил эти входные данные как «непостоянные», и совместимость с (значительной частью) существующего кода C была ранней целью C ++.

Некоторые API-интерфейсы UNIX, например getopt, действительно манипулируют argv[], поэтому это невозможно сделать и constпо этой причине.

(В сторону: интересно, хотя getoptпрототип предполагает, что он не будет изменять, argv[]но может изменять строки, на которые указывает, страница руководства Linux указывает, что getoptпереставляет свои аргументы, и, похоже, они знают, что они непослушны . Страница руководства на Open Группа не упоминает об этой перестановке.)

Положив constна argcи argvне будет покупать много, и это может сделать недействительными некоторые старой школы программирования практики, такие как:

// print out all the arguments:
while (--argc)
    std::cout << *++argv << std::endl;

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


1
-1 Re «Некоторые UNIX API, такие как getopt, действительно манипулируют argv [], поэтому и по этой причине его нельзя сделать константным.». Можно свободно добавлять верхний уровень constк argv, не влияя на то , что любая функция C может сделать. Также getoptзаявлен как int getopt(int argc, char * const argv[], const char *optstring);. И здесь constэто не верхний уровень, но объявляет указатели const, обещание не изменять их (хотя строки, на которые они указывают, могут быть изменены).
Приветствия и hth. - Alf

2
Прошу прощения, документы, которые я просмотрел , противоречивы: подпись не допускает такой перестановки. См. Пример . Однако в следующем тексте говорится, что это действительно перестановка. Итак, я снимаю голос против.
Приветствия и hth. - Alf

1
То есть вам нужно будет внести некоторые изменения, чтобы «разблокировать», чтобы я мог убрать голос против. И нет, вы неправильно понимаете прототип. Посмотрите на приведенный мною пример и ошибку компиляции «назначение местоположения только для чтения».
Приветствия и hth. - Alf

1
@ Cheersandhth.-Альф: Это интересно ... Я нашел прототип в заголовке, и он char* const* ___argvтам есть, но интерфейс действительно соответствует const char **. Такая путаница. Я перепутал приоритетность []и по *отношению к const. Мои извинения.
Joe Z

1
К вашему сведению: GNU getopt()знает, что он не следует стандарту, потому что он обращает внимание на POSIXLY_CORRECTпеременную среды, чтобы она работала правильно. В частности, POSIX не переставляет аргументы и не ищет параметры после первого аргумента, не являющегося параметром.
Джонатан Леффлер

36

Стандарт C (ISO / IEC 9899: 2011) гласит:

5.1.2.2.1 Запуск программы

¶1 Названа функция, вызываемая при запуске программы main. Реализация не объявляет прототипа для этой функции. Он должен быть определен с возвращаемым типом int и без параметров:

int main(void) { /* ... */ }

или с двумя параметрами (называемыми здесь argcи argv, хотя могут использоваться любые имена, поскольку они являются локальными для функции, в которой они объявлены):

int main(int argc, char *argv[]) { /* ... */ }

или эквивалент; 10) или каким-либо другим способом, определяемым реализацией.

¶2 Если они объявлены, параметры mainфункции должны подчиняться следующим ограничениям:

  • Значение argcдолжно быть неотрицательным.
  • argv[argc] должен быть нулевым указателем.
  • Если значение argcбольше нуля, элементы массива argv[0]до argv[argc-1]включительно должны содержать указатели на строки, которым среда хоста присваивает значения, определяемые реализацией, до запуска программы. Цель состоит в том, чтобы предоставить программе информацию, определенную перед запуском программы, из другого места в размещенной среде. Если среда хоста не способна предоставлять строки с буквами как в верхнем, так и в нижнем регистре, реализация должна гарантировать, что строки будут получены в нижнем регистре.
  • Если значение argcбольше нуля, строка, на которую указывает, argv[0] представляет имя программы; argv[0][0]должен быть нулевым символом, если имя программы недоступно в среде хоста. Если значение argcбольше единицы, строки, на которые указывает argv[1]through, argv[argc-1] представляют параметры программы.
  • Параметры argcи argvи строки, на которые указывает argvмассив, должны быть изменены программой и сохраняют свои последние сохраненные значения между запуском программы и ее завершением.

10) Таким образом, intможет быть заменено именем typedef, определенным как int, или тип argvможет быть записан как char **argv, и так далее.

Обратите внимание на последний пункт. В нем говорится, что и то argcи другое argvдолжно быть изменяемым. Их не нужно изменять, но они могут быть изменены.


Интересный. В частично связанной заметке я столкнулся с ошибкой, приводящей к сбою, которая оказалась вызвана тем, что QApplication(argc,argv)конструктор Qt был определен с argcпомощью ссылки . Это меня удивило.
HostileFork говорит, что не доверяйте SE

23

argcобычно не является константой, потому что подпись функции для main()предварительных дат const.

Поскольку argc является переменной стека, ее изменение не повлияет ни на что, кроме обработки вашей собственной командной строки.

Вы, конечно, можете заявить об этом, constесли хотите.


8

Верхний уровень constформального аргумента не является частью типа функции. Вы можете добавить или удалить его по своему усмотрению: он влияет только на то, что вы можете делать с аргументом в реализации функции.

Так что argcвы можете свободно добавлять const.

Но ведь argvвы не можете создать символьные данные, constне изменив тем самым сигнатуру функции. Это означает, что тогда это не одна из стандартных mainсигнатур функции, и ее не нужно будет распознавать как mainфункцию. Итак, не лучшая идея.


Хорошая причина не использовать стандартные mainаргументы в программах, не являющихся игрушечными, заключается в том, что в Windows они не могут представлять фактические аргументы программы, такие как имена файлов с международными символами. Это потому, что в Windows они очень строго кодируются как Windows ANSI. В Windows вы можете реализовать еще несколько переносимых средств доступа к аргументам в терминах GetCommandLineфункции API.


Подводя итог, ничто не мешает вам добавлять constк argc, но самый полезный const-ness на argvдаст вам нестандартную mainфункцию, скорее всего , не признаются в качестве таковых. К счастью (по иронии судьбы), есть веская причина не использовать стандартные mainаргументы для переносимого серьезного кода. Проще говоря, для практических целей они поддерживают только старый ASCII, содержащий только буквы английского алфавита.


1
Если вы разрабатываете для Windows с помощью cygwin или другой библиотеки, которая должным образом поддерживает UTF-8 (насколько я знаю, в настоящее время cygwin является единственным, но у меня есть кто-то, кто работает над другим вариантом), стандартная mainподпись работает нормально, и вы можете получать произвольные аргументы Unicode.
R .. GitHub НЕ ПОМОГАЕТ ICE

@R они также отлично работают на большинстве платформ * nix. вопрос не в том, есть ли платформы, на которых они работают. но, очень хорошая инициатива , и как это происходит я делаю то же самое, что более или менее серьезно
Приветствия и НТН. - Alf

4

Подпись mainявляется своего рода историческим артефактом из C. Исторически Cне было const.

Однако вы можете объявить свой параметр, constпоскольку константа влияет только на время компиляции.


Когда вы говорите «исторический C», насколько историческим мы здесь говорим?
Joe Z

8
constбыл добавлен в C89 / C90; до этого он не был частью C.
Джонатан Леффлер

@JonathanLeffler: Знаешь, я забыл об этом. Я выучил C в 1992 году. До этого я был хакером на языках Pascal, BASIC и ассемблере.
Joe Z

2

Потому argcчто это локальная переменная (а в C ++ не ссылка или что-то в этом роде), и потому, что особое место mainозначает, что махинации с обратной совместимостью предоставляют ей огромную свободу действий без веских причин заставлять приложения делать ее константой.

main() {}

int main() {}

main() { return 0; }

main(int argc, char* argv[]) { return 0; }

int main(const int argc, const char** argv) { /* no return*/ }

эти и многие другие варианты будут компилироваться на широком спектре компиляторов C и C ++.

Так что в конечном итоге дело не в том, что argc не является const, просто это не обязательно, но может быть, если вы хотите, чтобы это было.

http://ideone.com/FKldHF , пример C:

main(const int argc, const char* argv[]) { return 0; }

http://ideone.com/m1qc9c , пример C ++

main(const int argc) {}

Обратите внимание, что варианты без явного возвращаемого типа больше не действительны в C99, не говоря уже о C11, хотя компиляторы продолжают разрешать их по причинам обратной совместимости. Компиляторы C ++ не должны принимать варианты без возвращаемого типа.
Джонатан Леффлер,

@JonathanLeffler Ага, но последний пример ideone ( ideone.com/m1qc9c ) - это G ++ 4.8.2 с -std=c++11. Clang также принимает это, но дает warning: only one parameter on 'main' declaration [-Wmain], и MSVC протестует только по поводу отсутствия спецификатора возвращаемого типа и отсутствия ссылки на argc. Снова «махинации» и «
свобода

1

Помимо исторических причин, хорошая причина оставить argc и argv non- constсостоит в том, что реализация компилятора не знает, что вы собираетесь делать с аргументами для main, она просто знает, что она должна предоставить вам эти аргументы.

Когда вы определяете свои собственные функции и связанные с ними прототипы, вы знаете, какие параметры вы можете создать, constа какие - ваша функция будет изменять.

Взятый до крайности, вы могли бы заявить , что все параметры для всех функций должны быть объявлены const, а затем , если у вас есть причина , чтобы изменить их (например , уменьшение индекса для поиска через массив), то вы должны сделать местные Непро- constпеременным и скопируйте constзначения аргументов в эти переменные. Это создает напряженную работу и лишние LOC без реальной выгоды. Хороший статический анализатор подберет, если вы не изменяете значение аргумента, и порекомендует вам сделать параметр const.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.