Чи може argc дорівнювати нулю в системі POSIX?


78

Враховуючи стандартне визначення основної програми:

За яких обставин argcу системі POSIX може бути нуль?


12
Я спробував би int execv(const char *path, char *const argv[]);з цим argvмістити лише NULLвказівник;)

4
стандарт C дозволяє argcбути < 1, якщо це саме 0я знайшов тут port70.net/~nsz/c/c11/n1570.html#5.1.2.2.1p2
Ахал

13
Враховуючи широко використовуване if (argc < x) { fprintf(stderr, "Usage: %s ...", argv[0]); }, я однозначно бачу практичну актуальність цього питання :)

3
було б ефективним обманом stackoverflow.com/questions/8113786/…, якби не точна умова, що це має бути про POSIX
underscore_d

5
@mtraceur argv[0] || ""- це, на жаль, інт ...

Відповіді:


94

Так, це можливо. Якщо ви викликаєте свою програму наступним чином:

Або по черзі:

Тоді в "myprog" argcбуде 0.

Стандарт також спеціально допускає 0, argcяк зазначено у розділі 5.1.2.2.1 щодо запуску програми в розміщеному середовищі:

1 Функція, що викликається під час запуску програми, називається main. Реалізація не оголошує прототипу для цієї функції. Він повинен визначатися з типом повернення intта без параметрів:

або з двома параметрами (тут іменуються argcі argv, хоча можуть бути використані будь-які імена, оскільки вони є локальними для функції, в якій вони оголошені):

або еквівалент; або якимось іншим способом, визначеним реалізацією.

2 Якщо вони оголошені, параметри mainфункції повинні відповідати наступним обмеженням:

  • Значення argcмає бути невід’ємним.
  • argv[argc] повинен бути нульовим покажчиком.

...

Зауважте також, що це означає, що якщо argc0, то argv[0]гарантовано буде NULL. Однак, як printfтрактується покажчик NULL, коли він використовується як аргумент %sспецифікатора, стандарт не визначає. У цьому випадку багато реалізацій виведуть "(null)", але я не вірю, що це гарантовано.


7
@SylvainLeroux Ви (і інші читають цю відповідь пізніше) може виявитися цікаво знати, на додаток до документації про загальному випадку, що станом на пару років назад, одна система , яка повинна бути POSIX-сумісних вже фактично зробив неможливо викликати програми з argc == 0: OpenBSD.
mtraceur

@mtraceur, з цікавості: що робить OpenBSD, якщо ви телефонуєте execлише із шляхом та без аргументів для програми? (помилка? скопіювати шлях до нульового аргументу?)
ilkkachu

1
@ilkkachu У OpenBSD execveне вдається виконати помилку, EINVALякщо ви викликаєте її з порожнім argv. Це легко пропустити на execveсторінці керівництва , оскільки поведінка помилок для цієї умови згадується лише у списку можливих помилок внизу.
mtraceur

@mtraceur Цікаво, оскільки FreeBSD це дозволяє.
dbush

1
@mtraceur НУЛЬ у частині varargs. 0 був би покажчиком int, а не int.
Антті Хаапала,

22

Щоб додати до інших відповідей, у C (POSIX чи ні) нічого не заважає викликати main () як функцію в програмі.


10
... Га. Я думав, що це спеціально заборонено, але виявляється, це робить лише C ++, і C це добре.
Даніель Х,

19

Так, це може бути нуль, це означає argv[0] == NULL.

Це конвенція, яка argv[0]називається програмою. Ви можете мати, argc == 0якщо запустите собі двійковий файл, як із сім’єю execve і не аргументуєте. Ви навіть можете вказати рядок, який ніде не буде назвою програми. Ось чому використання argv[0]для отримання назви програми не зовсім надійне.

Зазвичай оболонка, куди ви вводите свій командний рядок, завжди додає назву програми як перший аргумент, але знову ж таки, це домовленість. Якщо argv[0]== "--help" і ви використовуєте опцію getopt для синтаксичного аналізу, ви не виявите його, оскільки optindвін ініціалізований до 1, але ви можете встановити optindзначення 0, getoptз'явиться опція use і "help" long.

коротенько: Це цілком можливо мати argc == 0( argv[0]не є насправді особливим). Це трапляється, коли панель запуску взагалі не дає аргументів.


"але ви можете встановити optind на 0" - Не портативно. POSIX говорить: "Якщо програма встановлює optind на нуль перед викликом getopt (), поведінка не вказана."

6
Ну, '--help' - це допустима назва файлу, тож чому argv [0] не може бути '--help'?
Том

2
Формулювання "це конвенція" дещо неточне. Програма, яка не відповідає цій «конвенції», не відповідає POSIX (прочитайте частину документації «Обґрунтування»). Тому, хоча дуже можливо зробити щось інше, це не відповідає нормам. Чи варто це розраховувати (чи можливо це), чи ні - це питання дискусій. Я в команді "не підтримувати зламане програмне забезпечення".
Деймон

3
@CharlesDuffy: Ось як ми застрягли з супом тегів (режим химерних HTML), спамом електронної пошти (зловживання SMTP) та регулярним входом до Інтернету випадкових країн (поганий BGP штовхає). Якщо хтось порушує стандарт, і немає жодної історичної причини, щоб дозволити їм це зробити, тоді ви повинні зазнати невдачі. Голосно.
Кевін

1
@Damon Обґрунтування не є нормативним, і це не буде перший раз, коли обґрунтування подає претензію, не підкріплену нормативним текстом. Я не можу знайти жодного нормативного тексту, в якому сказано, що передача нульового вказівника на назву програми не відповідає.

15

Ранні пропозиції вимагали, щоб значення argc, передане main (), було "один або більше". Це зумовлено тією ж вимогою у проектах стандарту ISO C. Насправді, історичні реалізації передали значення нуля, коли жодним аргументам не надається виклику функцій exec. Ця вимога була вилучена зі стандарту ISO C, а згодом також вилучена з цього обсягу POSIX.1-2017. Формулювання, зокрема використання цього слова, вимагає від строго відповідного додатка POSIX передавати принаймні один аргумент функції exec, гарантуючи тим самим, що argc буде одним або більшим при виклику такою програмою. Насправді це хороша практика, оскільки багато існуючих додатків посилаються на argv [0] без попередньої перевірки значення argc.

Вимога щодо строго відповідаючої програми POSIX також говорить, що значення, передане як перший аргумент, має бути рядком імені файлу, пов’язаним із запущеним процесом. Хоча деякі існуючі програми в деяких випадках передають ім'я шляху, а не рядок імені файлу, рядок імені файлу є більш корисним, оскільки загальне використання argv [0] використовується для діагностики друку. У деяких випадках передане ім'я файлу не є фактичним ім'ям файлу; наприклад, багато реалізацій утиліти входу використовують домовленість префікса ('-') до фактичного імені файлу, що вказує інтерпретатору команд, що викликається, що це "оболонка входу".

Також зверніть увагу, що тест і [утиліти вимагають певних рядків для аргументу argv [0], щоб мати детерміновану поведінку у всіх реалізаціях.

Джерело


Чи може argc дорівнювати нулю в системі POSIX?

Так, але це не буде суворо відповідати POSIX.


2
Чи означає "система POSIX", що кожна окрема програма в системі є "суворо відповідає програмі POSIX"? Справжнє запитання - я не знаю педантичної семантики стандарту щодо цього питання. Я знаю, що можу взяти систему, яка сертифікована як система POSIX, і тривіально написати / запустити на ній програму, яка не є "Строго відповідає програмі POSIX", і я не впевнений, що це передбачуване значення Стандарт POSIX, який говорить про те, що система стає невідповідною того моменту, коли на ній встановлено будь-яку несуворо невідповідну сторонній додаток?
mtraceur

@mtraceur Хіба це не зовсім окреме питання? Не впевнений, чи очікуєте ви відповіді на нього у цій темі коментарів або якщо вам потрібна додаткова інформація до відповіді. І те, і інше здається дивним.
труба

@mtraceur, якщо ваша програма працює на Linux, ви можете очікувати, що ваша програма буде працювати за стандартом Posix, відповідальність покладається на програму, яка виконує вас, щоб відповідати Posix, тобто вашу оболонку загалом. Тим не менш, ніщо не заважає оболонці не відповідати posix, але я дуже сумніваюся, що хтось буде її використовувати;), якщо ваш дистрибутив linux дозволяє невідповідну програму Posix у своїх пакетах, це не проблема linux.
Stargateur

@pipe Я прошу пояснити з цього приводу, тому що, на мою думку, це визначатиме, чи неявні міркування у цій відповіді стосуються питання чи вводять в оману: мені здається, відповідь "Так, але це не буде суворо відповідати POSIX" логічно означати, що сама присутність (або, можливо, виконання) будь-якої програми, яка може працювати char *nothing = { 0 }; execve(*prog, nothing, nothing)в системі, призведе до того, що POSIX оголосить цю систему "суворо невідповідною". Мені здається, що це навряд чи буде таким, як передбачав стандарт POSIX?
mtraceur

@Stargateur Чи справді ми можемо цього очікувати? Скажімо, ми пишемо програму C, яка є "суворо відповідним додатком POSIX", і запускаємо її на Linux, FreeBSD або одній із найновіших систем HP-UX або Solaris (на будь-якому іншому Unix, що відповідає POSIX). Отже, ми отримали наш суворо відповідний додаток POSIX в системі POSIX: тепер з’являється інший розробник, пише невеличку програму, яка викликає нашу програму із execveсистемним викликом - але цей розробник робить невелику помилку і закликає нас без будь-яких аргументів: справедливо сказати, що система в цілому більше не є "суворо відповідає POSIX"?
mtraceur

3

всякий раз, коли ви хочете запустити будь-який виконуваний файл, як-от ./a.outвін матиме один аргумент, який program name. Але можна запустити програму argcяк zeroу Linux, виконавши її з іншої програми, яка викликає execvза допомогою empty argument list.

наприклад,


3

TL; DR: Так, argv[0]може бути NULL, але не з якоїсь вагомої / розумної причини, про яку я знаю. Однак є причини не дбати про те, чи argv[0]має значення NULL, і спеціально дозволити процесу збій, якщо він є.


Так, argv[0]може мати значення NULL в системі POSIX, лише тоді, коли воно було виконано без будь-яких аргументів.

Більш цікавим практичним питанням є, чи повинна ваша програма піклуватися .

Відповідь на це: "Ні, ваша програма може припустити argv[0] , що не NULL" , оскільки деякі системні утиліти (утиліти командного рядка) або не працюють, або працюють недетермінованим чином, коли argv[0] == NULL, але що більш важливо, немає хорошого причина (крім дурості чи підлих цілей), чому будь-який процес це робить. (Я не впевнений, що стандартне використання getopt()також не вдається тоді, але я не сподівався б, що це спрацює.)

Багато коду, і справді більшість прикладів та утиліт, які я пишу, починаються з еквівалента

і це розумно і прийнятно, оскільки немає жодної вагомої причини для того, щоб процес виконував інший процес, не надаючи принаймні виконуваного шляху команди, тобто, execlp(cmd, cmd, NULL)а не execlp(cmd, NULL).

(Однак я можу подумати про кілька неприємних причин, таких як використання вікон гонки синхронізації, пов'язаних з командами pipe або socket: злий процес надсилає злий запит через встановлений сокет домену Unix, а потім негайно замінює себе уповноваженою командою жертви (запустити без будь-яких аргументів, щоб забезпечити мінімальний час запуску), так що коли служба, яка отримує запит, перевіряє облікові дані однолітків, вона бачить команду жертви, а не початковий злий процес. На мій погляд, для такої жертви найкраще команди, щоб швидко і швидко розбитися ( SIGSEGVшляхом відмінювання посилання на NULL-вказівник), а не намагатися поводитись «приємно», надаючи злому процесу більший часовий проміжок.)

Іншими словами, в той час як це можливо для процесу , щоб замінити себе іншим , але без будь - яких аргументів , що викликають argcдо нуля, така поведінка є необгрунтованим, в строгому сенсі , що не існує ніякого відомого без огидною причина для цього.

Через це, а також через те, що я люблю ускладнювати життя недоброзичливим і невпевненим програмістам та їх програмам, я особисто ніколи не додам тривіальну перевірку, подібну до

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


Чи вважаєте Ви перевіряти програми, щоб перевірити, чи написані вони з пильною увагою до кутових кейсів, як добрий / розумний / нехитрий варіант використання? Оскільки я регулярно посилаюся на кожну нову програму, яку я використовую, з нульовими аргументами, щоб швидко оцінити, яку поведінку я можу очікувати від програми за інших обставин "не повинно відбуватися / не повинно бути". У мене навіть є інструмент командного рядка, щоб я міг робити це швидко і легко, не маючи необхідності писати код кожного разу для нього (хоча, правда, я писав інструмент більше для контролю нульового аргументу загалом).
mtraceur

@mtraceur: Загалом так, але в цьому конкретному випадку ні. Це просто тому, що, на мою думку, у випадку без аргументів немає належних випадків використання (про які я знаю), але принаймні один підлий варіант використання; а нечесний варіант використання найкраще перемогти програмою, яка вмирає якомога раніше (і завдяки SIGSEGV це хороший спосіб зробити це). Я не думаю, що перевірка аргументу no-argument є будь-яким показником того, чи поводиться програма в іншому випадку нормально в ситуаціях типу "не повинно відбуватися" .
Номінальна тварина

@mtraceur: Одним з прикладів є write()(функція C низького рівня; обгортка syscall в Linux), що повертає від'ємне значення, відмінне від -1. Це просто не повинно відбуватися, тому більшість програмістів не тестують на це. Однак це сталося в Linux через помилку файлової системи ядра для записів понад 2 ГіБ. (На даний час кількість записів обмежується трохи менше 2 ГіБ на рівні syscall, щоб уникнути подібних помилок в інших драйверах файлової системи.) Мій власний код - єдиний, який я бачив, який також перевіряє цей випадок. (Я сприймаю це як EIOпомилку.) Однак, як я вже сказав, я вирішив не перевіряти наявність argc == 0свого коду.
Номінальна тварина

Я вирішив поставити +1 вашій відповіді, окрім нашої дискусії, оскільки я вважаю, що ваша перспектива та підхід цінні. Я бажаю, щоб більше людей так критично і ретельно замислювались над тим, чи впоратись (чи не впоратись) з усім спектром наріжних справ, які можуть трапитися.
mtraceur

Виправте мене, якщо я помиляюся: чи насправді стандарт C гарантує будь-яку розумну поведінку, не кажучи вже про те, щоб померти якомога раніше, якщо ви відмінитесь argv[0]коли argc == 0? Хіба це не розмежування посилань на нульовий покажчик, що, у свою чергу, означає невизначену поведінку? Вам комфортно довіряти, що кожна реалізація C, з якою буде змушений ваш код, буде виконувати щось розумне?
mtraceur
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.