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?
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?
argc
zu sein < 1
, wenn es genau so ist, dass 0
ich es hier gefunden habe port70.net/~nsz/c/c11/n1570.html#5.1.2.2.1p2
if (argc < x) { fprintf(stderr, "Usage: %s ...", argv[0]); }
sehe ich definitiv die praktische Relevanz dieser Frage :)
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:
1 Die beim Programmstart aufgerufene Funktion wird benannt
main
. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp vonint
und ohne Parameter definiert werden:
int main(void) { /* ... */ }
oder mit zwei Parametern (hier als
argc
und bezeichnetargv
, obwohl beliebige Namen verwendet werden können, da sie lokal für die Funktion sind, in der sie deklariert sind):
int main(int argc, char *argv[]) { /* ... */ }
oder gleichwertig; oder auf eine andere implementierungsdefinierte Weise.
2 Wenn sie deklariert sind, müssen die Parameter der
main
Funktion die folgenden Einschränkungen erfüllen:
- Der Wert von
argc
ist nicht negativ.argv[argc]
soll ein Nullzeiger sein....
Beachten Sie auch, dass dies bedeutet, dass wenn argc
0 argv[0]
ist, garantiert NULL ist. Wie printf
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.
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 aufrufen argv
. Es ist auf der execve
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;
}
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 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 optind
auf 1 initialisiert ist. Sie können sie jedoch optind
auf 0 setzen. Verwenden Sie getopt
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.
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.
Kann argc auf einem POSIX-System Null sein?
Ja, aber es würde nicht strikt POSIX entsprechen.
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: das program name
. Es ist jedoch möglich, ein Programm argc
wie zero
unter Linux auszuführen, indem es von einem anderen Programm ausgeführt wird, das execv
mit einem aufruft empty argument list
.
für zB
int main() {
char *buf[] = { NULL };
execv("./exe", buf); /* exe is binary which it run with 0 argument */
return 0;
}
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
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.
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 EIO
Fehler.) Wie gesagt, ich habe mich entschieden, argc == 0
meinen Code nicht zu überprüfen .
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?
int execv(const char *path, char *const argv[]);
mit einemargv
nur einenNULL
Zeiger zu finden, um herauszufinden;)