Чому аргумент не є постійною?


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

Оскільки в пункті № 3 ефективного C ++ зазначено "Використовувати const, коли це можливо", я починаю думати "чому б не зробити ці" постійні "параметриconst ".

Чи є сценарій, коли значення програми argcзмінюється в програмі?


38
Ви можете оголосити argcяк const.
Олівер Чарльворт

14
Я бачив багато код якості продукції, який це робить:--argc
Aniket Inge

2
Я думаю, що це стосується спадщини C, але не знаю як.
Луїс Мачука

13
Я підозрюю, що "Використовувати const, коли це можливо" відноситься до речей, переданих посиланням, де будь-які зміни в межах функції зберігаються, коли функція закінчиться. Це не відповідає дійсності, переданій за значенням, як ціле число, тому нічого не можна отримати, зробивши це const; Дійсно, передача argcяк const intзасіб, який ви потім не можете використовувати argcяк, скажімо, лічильник всередині функції.
Стів Мельников

4
@SteveMelnikoff: Я не погоджуюся, що нічого не можна отримати, зробивши constпараметр « прохідне значення». Див , наприклад , stackoverflow.com/a/8714278/277304 і stackoverflow.com/a/117557/277304
leonbloy

Відповіді:


114

У цьому випадку історія є фактором. 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;

Я писав такі програми в С, і знаю, що не один. Я кудись скопіював приклад .


1
-1 Re "Деякі API UNIX, такі як getopt, насправді маніпулюють argv [], тому це не може бути зроблено const і з цієї причини." Можна вільно додавати верхній рівень constдо цього argv, не впливаючи на те, що може виконувати будь-яка функція C. Також getoptоголошено як int getopt(int argc, char * const argv[], const char *optstring);. І тут constце не верхній рівень, але оголошує покажчики такими const, що обіцяють їх не змінювати (хоча рядки, на які вони вказують, можливо, можуть бути змінені).
ура та хт. - Альф

@ Cheersandhth.-Alf: getopt переставляє в argv[]масив за замовчуванням, але не змінює рядки. (Слово перестановка походить безпосередньо зі сторінки man.) Це означає, що сам argvмасив constнавіть не є, якщо є рядки, на які він вказує. Як permuting argv[]масив не маніпулює ним?
Джо Z

1
Вибачте, документи, які я подивився , несуперечливі: підпис не дозволяє такої перестановки. Див. Приклад . Однак у наступному тексті йдеться про перестановку. Отже, я знімаю протилежну заяву.
ура та хт. - Альф

1
Тобто вам доведеться виконати деяку редагування, щоб "розблокувати", щоб я міг видалити нижчу заявку. І ні, ви нерозумієте прототип. Подивіться на приклад, який я надав, і помилка компіляції, "присвоєння місця лише для читання".
ура та хт. - Альф

1
@ Cheersandhth.-Alf: Цікаво ... Я шукав прототип у заголовку, і він є char* const* ___argvтам, але інтерфейс насправді дотримується const char **. Така плутанина. Я переплутав пріоритет []і *відносно цього const. Мої вибачення.
Джо Z

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]через, 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 можна реалізувати ще якийсь портативний інструмент доступу до аргументів з точки зору функції GetCommandLineAPI.


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


1
Якщо ви розробляєте для Windows з cygwin або іншим libc, який належним чином підтримує UTF-8 (наскільки я знаю, cygwin є єдиним на даний момент, але у мене хтось працює над іншим варіантом), стандартний mainпідпис працює добре, і ви можете отримувати довільні аргументи Unicode.
R .. GitHub СТОП ДОПОМОГАТИ ДІЯ

@R вони також чудово працюють на більшості * nix платформ. питання не в тому, чи існують платформи, де вони працюють. але, дуже приємна ініціатива , і, як це буває, я роблю те саме, більш-менш серйозно
Ура та хт. - Альф

4

Підпис mainдещо історичного артефакту C. Історично Cне мали const.

Однак ви можете оголосити свій параметр, constоскільки ефекти const - це лише час компіляції.


Коли ви говорите "історичний С", наскільки тут ми говоримо історично?
Джо Z

8
constдодавали до C89 / C90; до цього він не входив до складу C.
Джонатан Леффлер

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

2

Тому що argcце локальна змінна (і, в С ++, не посилання чи щось подібне), і тому, що особливе місце 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 ++.

Отже, в кінцевому підсумку це не те, що аргумент не 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.com/m1qc9c ) - це G ++ 4.8.2 з -std=c++11. Clang також приймає це, але дає warning: only one parameter on 'main' declaration [-Wmain], а MSVC протестує лише щодо відсутнього специфікатора типу return і відсутності посилання на argc. Знову: «shenanigans» та «leeway» :)
kfsone

1

Окрім історичних причин, вагомою причиною утримати constаргументи та аргументи не є те, що реалізація компілятора не знає, що ви збираєтеся робити з аргументами для основного, він просто знає, що він повинен надати вам ці аргументи.

Коли ви визначаєте власні функції та пов'язані з ними прототипи, ви знаєте, які параметри ви можете зробити const та які саме змінити ваша функція.

У крайньому випадку ви можете оголосити, що всі параметри всіх функцій повинні бути оголошені const, і тоді, якщо у вас була причина змінити їх (наприклад, зменшення індексу для пошуку через масив), вам доведеться зробити локальні не constзмінні і скопіюйте значення constаргументів у ці змінні. Це робить для зайнятої роботи та додаткового LOC без реальної вигоди. Пристойний статичний аналізатор підбере, якщо ви не змінюєте значення аргументу, і рекомендує ввести параметр const.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.