Kann argc auf einem POSIX-System Null sein?

78

Angesichts der Standarddefinition für das Hauptprogramm:

Unter welchen Umständen kann argcauf einem POSIX-System Null sein?

Sylvain Leroux
quelle
12
Ich würde versuchen, int execv(const char *path, char *const argv[]);mit einem argvnur einen NULLZeiger zu finden, um herauszufinden;)
4
Der C-Standard erlaubt es argczu sein < 1, wenn es genau so ist, dass 0ich es hier gefunden habe port70.net/~nsz/c/c11/n1570.html#5.1.2.2.1p2
Achal
13
Angesichts der weit verbreiteten if (argc < x) { fprintf(stderr, "Usage: %s ...", argv[0]); }sehe ich definitiv die praktische Relevanz dieser Frage :)
3
wäre ein effektiver Betrüger von stackoverflow.com/questions/8113786/…, wenn nicht genau angegeben wäre, dass es sich um POSIX handeln muss
underscore_d
5
@mtraceur argv[0] || ""ist leider ein int ...

Antworten:

94

Ja, es ist möglich. Wenn Sie Ihr Programm wie folgt aufrufen:

Oder alternativ:

Dann wird in "myprog" argc0 sein.

Der Standard erlaubt auch speziell eine 0, argcwie in Abschnitt 5.1.2.2.1 bezüglich des Programmstarts in einer gehosteten Umgebung angegeben:

1 Die beim Programmstart aufgerufene Funktion wird benannt main. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp von intund ohne Parameter definiert werden:

oder mit zwei Parametern (hier als argcund bezeichnet argv, obwohl beliebige Namen verwendet werden können, da sie lokal für die Funktion sind, in der sie deklariert sind):

oder gleichwertig; oder auf eine andere implementierungsdefinierte Weise.

2 Wenn sie deklariert sind, müssen die Parameter der mainFunktion die folgenden Einschränkungen erfüllen:

  • Der Wert von argcist nicht negativ.
  • argv[argc] soll ein Nullzeiger sein.

...

Beachten Sie auch, dass dies bedeutet, dass wenn argc0 argv[0]ist, garantiert NULL ist. Wie printfein NULL-Zeiger behandelt wird, wenn er als Argument für einen Bezeichner verwendet %swird, ist im Standard jedoch nicht festgelegt. Viele Implementierungen geben in diesem Fall "(null)" aus, aber ich glaube nicht, dass dies garantiert ist.

dbush
quelle
6
@SylvainLeroux Sie (und andere lesen Sie diese Antwort später) finden es vielleicht zu wissen , interessant, zusätzlich zu der Dokumentation über den allgemeinen Fall, dass von ein paar Jahren, ein System , dass Ziele POSIX-konform zu sein hat in der Tat gemacht Es ist unmöglich, Programme mit argc == 0OpenBSD aufzurufen .
mtraceur
@mtraceur, aus Neugier: Was macht OpenBSD, wenn Sie execnur mit dem Pfad und ohne Argumente für das Programm aufrufen ? (fehlgeschlagen? Kopieren Sie den Pfad zum nullten Argument?)
ilkkachu
1
@ilkkachu On OpenBSD schlägt execvemit dem Fehler fehl, EINVALwenn Sie es mit einem leeren aufrufen argv. Es ist auf der execveHandbuchseite leicht zu übersehen , da das Fehlerverhalten für diese Bedingung nur in der Liste der möglichen Fehler unten erwähnt wird.
mtraceur
@mtraceur Interessant, da FreeBSD es erlaubt.
dbush
1
@mtraceur der NULL in varargs Teil. 0 wäre ein int nicht Zeiger.
Antti Haapala
22

Um die anderen Antworten zu ergänzen, gibt es in C nichts (POSIX oder nicht), was verhindert, dass main () als Funktion innerhalb des Programms aufgerufen wird.

Opossum
quelle
10
... Huh. Ich dachte, das wäre ausdrücklich verboten, aber es stellt sich heraus, dass nur C ++ das tut und C damit einverstanden ist.
Daniel H
19

Ja, es kann Null sein, was bedeutet argv[0] == NULL.

Es ist eine Konvention, argv[0]die der Name des Programms ist. Sie können haben, argc == 0wenn Sie sich die Binärdatei starten, wie bei execve family und kein Argument angeben . Sie können sogar eine Zeichenfolge angeben, die nicht annähernd dem Programmnamen entspricht. Aus diesem Grund ist die Verwendung argv[0]zum Abrufen des Programmnamens nicht ganz zuverlässig.

Normalerweise fügt die Shell, in der Sie Ihre Befehlszeile eingeben, immer den Programmnamen als erstes Argument hinzu, aber auch dies ist eine Konvention. Wenn argv[0]== "--help" und Sie die Option getopt to parse verwenden , werden Sie diese nicht erkennen, da sie optindauf 1 initialisiert ist. Sie können sie jedoch optindauf 0 setzen. Verwenden Sie getoptund die Option "help" long wird angezeigt.

lange Rede, kurzer Sinn: Es ist durchaus möglich zu haben argc == 0( argv[0]ist an sich nicht wirklich etwas Besonderes). Es passiert, wenn der Launcher überhaupt keine Argumente liefert.

Toms
quelle
"aber Sie können optind auf 0 setzen" - Nicht portabel. POSIX sagt: "Wenn die Anwendung optind vor dem Aufruf von getopt () auf Null setzt , ist das Verhalten nicht angegeben."
6
Nun, '--help' ist ein legitimer Dateiname. Warum kann argv [0] nicht '--help' sein?
Toms
1
Der Wortlaut "es ist eine Konvention" ist etwas ungenau. Ein Programm, das dieser "Konvention" nicht entspricht, entspricht nicht POSIX (lesen Sie den Teil "Begründung" in der Dokumentation). Während es also sehr gut möglich ist, etwas anderes zu tun, ist dies nicht konform. Ob dies zu erwarten ist oder nicht (weil es möglich ist) oder nicht, steht zur Debatte. Ich bin im Team "unterstütze keine kaputte Software".
Damon
2
@CharlesDuffy: So haben wir uns mit Tag-Suppe (HTML-Macken-Modus), E-Mail-Spam (SMTP-Missbrauch) und dem regelmäßigen Internet-Blackholing von zufälligen Ländern (schlechte BGP-Pushs) festgefahren. Wenn jemand gegen den Standard verstößt und es keinen historischen Grund gibt, dies zuzulassen, sollten Sie scheitern. Laut.
Kevin
1
@Damon Die Begründung ist nicht normativ und es wäre nicht das erste Mal, dass die Begründung eine Behauptung aufstellt, die nicht durch den normativen Text gestützt wird. Ich kann keinen normativen Text finden, der besagt, dass das Übergeben eines Nullzeigers für den Programmnamen nicht konform ist.
15

Frühe Vorschläge erforderten, dass der an main () übergebene Wert von argc "eins oder größer" ist. Dies wurde durch die gleiche Anforderung in Entwürfen der ISO C-Norm vorangetrieben. Tatsächlich haben historische Implementierungen den Wert Null übergeben, wenn dem Aufrufer der exec-Funktionen keine Argumente übergeben wurden. Diese Anforderung wurde aus der ISO C-Norm entfernt und anschließend auch aus diesem Band von POSIX.1-2017 entfernt. Der Wortlaut, insbesondere die Verwendung des Wortes, erfordert, dass eine streng konforme POSIX-Anwendung mindestens ein Argument an die exec-Funktion übergibt, wodurch garantiert wird, dass argc eins oder mehr ist, wenn es von einer solchen Anwendung aufgerufen wird. Tatsächlich ist dies eine gute Praxis, da viele vorhandene Anwendungen auf argv [0] verweisen, ohne zuvor den Wert von argc zu überprüfen.

Die Anforderung an eine streng konforme POSIX-Anwendung besagt auch, dass der als erstes Argument übergebene Wert eine Dateinamenzeichenfolge ist, die dem zu startenden Prozess zugeordnet ist. Obwohl einige vorhandene Anwendungen unter bestimmten Umständen einen Pfadnamen anstelle einer Dateinamenzeichenfolge übergeben, ist eine Dateinamenzeichenfolge allgemeiner nützlich, da in der Druckdiagnose häufig argv [0] verwendet wird. In einigen Fällen ist der übergebene Dateiname nicht der tatsächliche Dateiname der Datei. Beispielsweise verwenden viele Implementierungen des Anmeldedienstprogramms eine Konvention, bei der dem tatsächlichen Dateinamen ein ('-') vorangestellt wird, das dem aufgerufenen Befehlsinterpreter anzeigt, dass es sich um eine "Anmeldeshell" handelt.

Beachten Sie außerdem, dass für das Test- und das [Dienstprogramm] bestimmte Zeichenfolgen erforderlich sind, damit das Argument argv [0] über alle Implementierungen hinweg deterministisches Verhalten aufweist.

Quelle


Kann argc auf einem POSIX-System Null sein?

Ja, aber es würde nicht strikt POSIX entsprechen.

Stargateur
quelle
2
Bedeutet "POSIX-System", dass "jedes einzelne Programm auf dem System eine" streng konforme POSIX-Anwendung "ist? Echte Frage - Ich kenne die pedantische Semantik des Standards in dieser Angelegenheit nicht. Ich weiß, dass ich ein System, das als POSIX-System zertifiziert ist, nehmen und trivial ein Programm darauf schreiben / ausführen kann, das keine "streng konforme POSIX-Anwendung" ist, und ich bin mir nicht sicher, ob es die beabsichtigte Bedeutung von ist POSIX-Standard, um zu sagen, dass das System in dem Moment, in dem eine nicht streng konforme Drittanbieteranwendung darauf installiert ist, nicht mehr konform ist?
mtraceur
@mtraceur Ist das nicht eine ganz andere Frage? Sie sind sich nicht sicher, ob Sie erwarten, dass es in diesem Kommentarthread beantwortet wird, oder ob Sie möchten, dass die zusätzlichen Informationen zur Antwort hinzugefügt werden. Beides scheint komisch.
Pipe
@mtraceur Wenn Ihr Programm unter Linux ausgeführt wird, können Sie davon ausgehen, dass Ihr Programm unter dem Posix-Standard ausgeführt wird. Die Verantwortung liegt bei dem Programm, das Sie ausführt, um Posix-konform zu sein, auch bekannt als Ihre Shell im Allgemeinen. Das heißt, nichts hindert eine Shell daran, nicht posix-konform zu sein, aber ich bezweifle sehr, dass jemand sie verwenden wird;), wenn Ihre Linux-Distribution ein nicht konformes Posix-Programm in ihren Paketen zulässt, ist dies nicht das Problem von Linux.
Stargateur
@pipe Ich bitte um Klarstellung in diesem Punkt, weil ich denke, dass dies bestimmen würde, ob die implizite Begründung in dieser Antwort auf die Frage zutrifft oder irreführend ist: Die Antwort "Ja, aber es würde nicht streng POSIX entsprechen" scheint mir logisch zu implizieren, dass das bloße Vorhandensein (oder möglicherweise die Ausführung) eines Programms, das char *nothing = { 0 }; execve(*prog, nothing, nothing)auf einem System ausgeführt werden kann, dazu führen würde, dass POSIX dieses System als "nicht streng konform" deklariert. Dies scheint mir unwahrscheinlich zu sein, was der POSIX-Standard beabsichtigt hat?
mtraceur
@ Stargateur Können wir das wirklich erwarten? Angenommen, wir schreiben ein C-Programm, das eine "streng konforme POSIX-Anwendung" ist, und führen es unter Linux, FreeBSD oder einem der neuesten HP-UX- oder Solaris-Systeme (unter jedem anderen Unix, das POSIX-konform ist) aus. Wir haben also unsere streng konforme POSIX-Anwendung auf einem POSIX-System: Jetzt kommt ein anderer Entwickler, schreibt ein kleines Programm, das unser Programm mit dem execveSystemaufruf aufruft - aber dieser Entwickler macht einen kleinen Fehler und ruft uns ohne Argumente auf: Is ist es fair zu sagen, dass das System als Ganzes nicht mehr "streng konform mit POSIX" ist?
mtraceur
3

Wann immer Sie eine ausführbare Datei wie ./a.outdiese ausführen möchten, gibt es ein Argument: das program name. Es ist jedoch möglich, ein Programm argcwie zerounter Linux auszuführen, indem es von einem anderen Programm ausgeführt wird, das execvmit einem aufruft empty argument list.

für zB

Achal
quelle
3

TL; DR: Ja, argv[0]kann NULL sein, aber nicht aus einem guten / vernünftigen Grund, den ich kenne. Es gibt jedoch Gründe, sich nicht darum zu kümmern, ob argv[0]NULL ist, und den Prozess speziell zum Absturz zu bringen, wenn dies der Fall ist.


Ja, argv[0]kann auf einem POSIX-System genau dann NULL sein, wenn es ohne Argumente ausgeführt wurde.

Die interessantere praktische Frage ist, ob sich Ihr Programm darum kümmert .

Die Antwort darauf lautet "Nein, Ihr Programm kann davon ausgehen, dass argv[0] es nicht NULL ist" , da einige Systemdienstprogramme (Befehlszeilenprogramme) entweder nicht oder nicht deterministisch funktionieren, wenn argv[0] == NULL, aber was noch wichtiger ist, es nichts Gutes gibt Grund (außer Dummheit oder schändlichen Zwecken), warum irgendein Prozess das tun würde. (Ich bin mir nicht sicher, ob die Standardverwendung von dann getopt()auch fehlschlägt - aber ich würde nicht erwarten, dass es funktioniert.)

Viel Code und in der Tat die meisten Beispiele und Dienstprogramme, die ich schreibe, beginnen mit dem Äquivalent von

und dies ist vernünftig und akzeptabel, da es keinen guten Grund für einen Prozess gibt, einen anderen Prozess auszuführen, ohne zumindest den ausgeführten Befehlspfad bereitzustellen, dh execlp(cmd, cmd, NULL)nichtexeclp(cmd, NULL) .

(Ich kann mir jedoch einige schändliche Gründe vorstellen, wie das Ausnutzen von Timing-Race-Fenstern im Zusammenhang mit Pipe- oder Socket-Befehlen: Ein böser Prozess sendet eine böse Anfrage über einen etablierten Unix-Domain-Socket und ersetzt sich dann sofort durch einen autorisierten Opferbefehl (Ausführen) ohne Argumente, um eine minimale Startzeit zu gewährleisten), sodass der Dienst, der die Anforderung erhält, die Peer-Anmeldeinformationen überprüft und den Opferbefehl anstelle des ursprünglichen bösen Prozesses sieht. Meiner Meinung nach ist dies für ein solches Opfer am besten Befehle zum schnellen und schnellen Absturz ( SIGSEGVdurch Dereferenzieren eines NULL-Zeigers), anstatt zu versuchen, sich "nett" zu verhalten, was dem bösen Prozess ein größeres Zeitfenster gibt.)

Mit anderen Worten, während es möglich ist , dass sich ein Prozess durch einen anderen ersetzt, ohne dass Argumente argcNull verursachen, ist ein solches Verhalten in dem strengen Sinne unangemessen, dass es keinen bekannten nicht schändlichen Grund dafür gibt.

Aus diesem Grund und der Tatsache, dass ich es liebe, schändlichen und lieblosen Programmierern und ihren Programmen das Leben schwer zu machen, werde ich persönlich niemals den trivialen Scheck hinzufügen, ähnlich wie

es sei denn, dies wird von jemandem angefordert, der einen bestimmten Anwendungsfall berücksichtigt. Und selbst dann wäre ich etwas misstrauisch und möchte zuerst den Anwendungsfall selbst überprüfen.

Nominelles Tier
quelle
Betrachten Sie die Überprüfung von Programmen, um festzustellen, ob sie unter sorgfältiger Berücksichtigung von Eckfällen geschrieben wurden, als einen guten / vernünftigen / nicht schändlichen Anwendungsfall? Da ich jedes neue Programm, das ich verwende, regelmäßig mit null Argumenten aufrufe, um schnell abzuschätzen, welche Art von Verhalten ich unter anderen Umständen vom Programm erwarten kann, sollte es nicht passieren / sollte nicht durchgeführt werden. Ich habe sogar ein Befehlszeilen-Tool , mit dem ich es schnell und einfach ausführen kann, ohne jedes Mal Code dafür schreiben zu müssen (obwohl ich das Tool zugegebenermaßen mehr zur Steuerung des nullten Arguments im Allgemeinen geschrieben habe).
mtraceur
@mtraceur: Im Allgemeinen ja, aber in diesem speziellen Fall nein. Dies liegt einfach daran, dass ich denke, dass der Fall ohne Argumente keine richtigen Anwendungsfälle (von denen ich weiß) hat, sondern mindestens einen schändlichen Anwendungsfall. und der schändliche Anwendungsfall wird am besten dadurch besiegt, dass das Programm so früh wie möglich stirbt (und aufgrund von SIGSEGV ist dies ein guter Weg, dies zu tun). Ich denke nicht, dass die Überprüfung des Falls ohne Argumente ein Hinweis darauf ist, ob sich das Programm in Situationen vom Typ "sollte nicht auftreten" ansonsten vernünftig verhält .
Nominelles Tier
@mtraceur: Ein Beispiel ist write()(Low-Level-C-Funktion; ein Syscall-Wrapper unter Linux), der einen anderen negativen Wert als -1 zurückgibt . Es sollte einfach nicht auftreten, daher testen die meisten Programmierer nicht darauf. Unter Linux ist es jedoch aufgrund eines Kernel-Dateisystemfehlers bei Schreibvorgängen über 2 GiB aufgetreten. (Derzeit sind Schreibvorgänge auf Syscall-Ebene auf knapp 2 GiB beschränkt, um ähnliche Fehler in anderen Dateisystemtreibern zu vermeiden.) Mein eigener Code ist der einzige, den ich gesehen habe, der auch diesen Fall überprüft. (Ich behandle es als EIOFehler.) Wie gesagt, ich habe mich entschieden, argc == 0meinen Code nicht zu überprüfen .
Nominelles Tier
Ich habe beschlossen, Ihre Antwort +1 zu geben, abgesehen von unserer Diskussion, weil ich Ihre Perspektive und Ihren Ansatz für wertvoll halte. Ich wünsche mir, dass mehr Menschen so kritisch und sorgfältig darüber nachdenken, ob sie mit der gesamten Bandbreite der möglichen Eckfälle umgehen sollen (oder nicht).
mtraceur
Korrigieren Sie mich, wenn ich falsch liege: Garantiert der C-Standard tatsächlich ein vernünftiges Verhalten, geschweige denn, so früh wie möglich zu sterben, wenn Sie argv[0]wann dereferenzieren argc == 0? Ist das nicht eine Dereferenzierung eines Nullzeigers, was wiederum undefiniertes Verhalten bedeutet? Vertrauen Sie darauf, dass jede Implementierung von C, gegen die Ihr Code ausgeführt wird, eine vernünftige Leistung erbringt?
mtraceur