Angesichts der Standarddefinition für das Hauptprogramm:
int main(int argc, char *argv[]) {
...
}
Unter welchen Umständen kann argc
auf einem POSIX-System Null sein?
c
posix
language-lawyer
main
Sylvain Leroux
quelle
quelle
int execv(const char *path, char *const argv[]);
mit einemargv
nur einenNULL
Zeiger zu finden, um herauszufinden;)argc
zu sein< 1
, wenn es genau so ist, dass0
ich es hier gefunden habe port70.net/~nsz/c/c11/n1570.html#5.1.2.2.1p2if (argc < x) { fprintf(stderr, "Usage: %s ...", argv[0]); }
sehe ich definitiv die praktische Relevanz dieser Frage :)argv[0] || ""
ist leider ein int ...Antworten:
Ja, es ist möglich. Wenn Sie Ihr Programm wie folgt aufrufen:
execl("./myprog", NULL, (char *)NULL);
Oder alternativ:
char *args[] = { NULL }; execv("./myprog", args);
Dann wird in "myprog"
argc
0 sein.Der Standard erlaubt auch speziell eine 0,
argc
wie in Abschnitt 5.1.2.2.1 bezüglich des Programmstarts in einer gehosteten Umgebung angegeben:Beachten Sie auch, dass dies bedeutet, dass wenn
argc
0argv[0]
ist, garantiert NULL ist. Wieprintf
ein NULL-Zeiger behandelt wird, wenn er als Argument für einen Bezeichner verwendet%s
wird, ist im Standard jedoch nicht festgelegt. Viele Implementierungen geben in diesem Fall "(null)" aus, aber ich glaube nicht, dass dies garantiert ist.quelle
argc == 0
OpenBSD aufzurufen .exec
nur mit dem Pfad und ohne Argumente für das Programm aufrufen ? (fehlgeschlagen? Kopieren Sie den Pfad zum nullten Argument?)execve
mit dem Fehler fehl,EINVAL
wenn Sie es mit einem leeren aufrufenargv
. Es ist auf derexecve
Handbuchseite leicht zu übersehen , da das Fehlerverhalten für diese Bedingung nur in der Liste der möglichen Fehler unten erwähnt wird.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.
int main(int argc, int argv[]) { if (argc == 0) printf("Hey!\n"); else main(0,NULL); return 0; }
quelle
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 == 0
wenn 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 Verwendungargv[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 sieoptind
auf 1 initialisiert ist. Sie können sie jedochoptind
auf 0 setzen. Verwenden Siegetopt
und 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.quelle
Quelle
Ja, aber es würde nicht strikt POSIX entsprechen.
quelle
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?execve
Systemaufruf 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?Wann immer Sie eine ausführbare Datei wie
./a.out
diese ausführen möchten, gibt es ein Argument: dasprogram name
. Es ist jedoch möglich, ein Programmargc
wiezero
unter Linux auszuführen, indem es von einem anderen Programm ausgeführt wird, dasexecv
mit einem aufruftempty argument list
.für zB
int main() { char *buf[] = { NULL }; execv("./exe", buf); /* exe is binary which it run with 0 argument */ return 0; }
quelle
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, obargv[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, wennargv[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 danngetopt()
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
int main(int argc, char *argv[]) { if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { printf("Usage: %s [ -h | --help ]\n", argv[0]); /* ... print usage ... */ return EXIT_SUCCESS; }
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 (
SIGSEGV
durch 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
argc
Null 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
static int usage(const char *argv0) { /* Print usage using argv0 as if it was argv[0] */ return EXIT_SUCCESS; } int main(int argc, char *argv[]) { if (argc < 1) return usage("(this)"); if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) return usage(argv[0]); /* argv[0] and argv[1] are non-NULL, argc >= 2 */
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.
quelle
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 alsEIO
Fehler.) Wie gesagt, ich habe mich entschieden,argc == 0
meinen Code nicht zu überprüfen .argv[0]
wann dereferenzierenargc == 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?