Kann argc auf einem POSIX-System Null sein?

Lesezeit: 9 Minuten

Benutzeravatar von Sylvain Leroux
Sylvain Leroux

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?

  • Ich würde es versuchen int execv(const char *path, char *const argv[]); mit einem argv enthält nur a NULL Zeiger um es herauszufinden 😉

    Benutzer2371524

    13. April 2018 um 12:46 Uhr

  • der C-Standard erlaubt die argc sein < 1wenn es genau so ist 0 Ich habe es hier gefunden port70.net/~nsz/c/c11/n1570.html#5.1.2.2.1p2

    – Achal

    13. April 2018 um 12:49 Uhr

  • Angesichts der weit verbreiteten if (argc < x) { fprintf(stderr, "Usage: %s ...", argv[0]); }Ich sehe auf jeden Fall die praktisch Relevanz dieser Frage 🙂

    Benutzer2371524

    13. April 2018 um 12:59 Uhr

  • wäre ein effektiver Betrüger von stackoverflow.com/questions/8113786/… wenn nicht die genaue Bestimmung wäre, dass es sich um POSIX handeln muss

    – Unterstrich_d

    13. April 2018 um 13:01 Uhr

  • @mtraceur argv[0] || "" ist leider ein int…

    Benutzer2404501

    13. April 2018 um 20:44 Uhr

Benutzeravatar von dbush
dbusch

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 in “myprog”, argc wird 0 sein.

Der Standard erlaubt auch ausdrücklich eine 0 argc wie in Abschnitt 5.1.2.2.1 zum Programmstart in einer gehosteten Umgebung erwähnt:

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 definiert werden int und ohne Parameter:

int main(void) { /* ... */ } 

oder mit zwei Parametern (hier als argc und argvobwohl 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, werden die Parameter an die main Die Funktion unterliegt den folgenden Einschränkungen:

  • Der Wert von argc soll nichtnegativ sein.
  • argv[argc] soll ein Nullzeiger sein.

Beachten Sie auch, dass dies bedeutet, dass wenn argc ist dann 0 argv[0] ist garantiert NULL. Wie printf behandelt einen NULL-Zeiger, wenn er als Argument für a verwendet wird %s Der Spezifizierer ist jedoch nicht im Standard aufgeführt. Viele Implementierungen geben in diesem Fall “(null)” aus, aber es ist nicht garantiert.

  • @SylvainLeroux Sie (und andere, die diese Antwort später lesen) finden es vielleicht interessant zu wissen, zusätzlich zu der Dokumentation über den allgemeinen Fall, dass vor ein paar Jahren eines System, das darauf abzielt, POSIX-konform zu sein hat machte es tatsächlich unmöglich, Programme damit aufzurufen argc == 0: OpenBSD.

    – mtraceur

    13. April 2018 um 22:28 Uhr


  • @mtraceur, aus Neugier: Was macht OpenBSD, wenn du anrufst exec nur mit dem Pfad und ohne Argumente für das Programm? (fehlgeschlagen? Kopieren Sie den Pfad zum nullten Argument?)

    – Ilkkachu

    13. April 2018 um 22:48 Uhr

  • @ilkkachu Auf OpenBSD execve scheitern mit dem Fehler EINVAL wenn Sie es mit einem leeren aufrufen argv. Es ist leicht zu übersehen execve Handbuchseiteda das Fehlerverhalten für diese Bedingung nur in der Liste der möglichen Fehler unten erwähnt wird.

    – mtraceur

    14. April 2018 um 0:25 Uhr

  • @mtraceur Interessant, da FreeBSD tut erlaube es.

    – dbusch

    14. April 2018 um 20:17 Uhr

  • @mtraceur die NULL im Varargs-Teil. 0 wäre ein Int-nicht-Zeiger.

    – Antti Haapala – Слава Україні

    17. April 2018 um 1:08 Uhr

Benutzeravatar von Possum
Opossum

Um die anderen Antworten zu ergänzen, gibt es nichts in C (POSIX oder nicht), das 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;
}

  • … Hm. Ich dachte, das wäre ausdrücklich verboten, aber es stellt sich heraus, dass nur C++ das tut und C damit einverstanden ist.

    – DanielH

    13. April 2018 um 22:33 Uhr

Benutzeravatar von Tom
Toms

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

Das ist eine Konvention argv[0] ist der Name des Programms. Du kannst haben argc == 0 wenn Sie die Binärdatei starten, wie mit ausführen Familie und streite nicht. Sie können sogar eine Zeichenfolge angeben, die bei weitem nicht der Programmname ist. Deshalb verwenden argv[0] den Namen des Programms zu bekommen ist nicht ganz zuverlässig.

Normalerweise fügt die Shell, in die Sie Ihre Befehlszeile eingeben, immer den Programmnamen als erstes Argument hinzu, aber auch hier handelt es sich um eine Konvention. Wenn argv[0] == “–help” und Sie verwenden getopt Option zu parsen, werden Sie es nicht erkennen, weil optind wird auf 1 initialisiert, aber Sie können festlegen optind auf 0 verwenden getopt und die lange Option “Hilfe” wird angezeigt.

Um es kurz zu machen: Es ist durchaus möglich, es zu haben argc == 0 (argv[0] ist an sich nichts Besonderes). Es passiert, wenn der Launcher überhaupt kein Argument gibt.

  • “aber Sie können optind auf 0 setzen” — Nicht portabel. POSIX sagt “Wenn die Anwendung setzt optieren vor dem Anruf auf Null getopt (), das Verhalten ist nicht spezifiziert.”

    Benutzer743382

    13. April 2018 um 13:48 Uhr


  • Nun, ‘–help’ ist ein legitimer Dateiname, also warum argv[0] kann nicht ‘–help’ sein?

    – Toms

    13. April 2018 um 14:24 Uhr

  • Die Formulierung “es ist eine Konvention” ist etwas ungenau. Ein Programm, das dieser „Konvention“ nicht folgt, ist nicht POSIX-konform (lesen Sie den „Grundlagen“-Teil der Dokumentation). Es ist also durchaus möglich, etwas anderes zu tun, aber es ist nicht konform. Ob dies etwas ist, mit dem man rechnen sollte (weil es möglich ist) oder nicht, ist umstritten. Ich bin im Team “Keine Unterstützung für defekte Software”.

    – Dämon

    13. April 2018 um 15:20 Uhr


  • @CharlesDuffy: So blieben wir bei Tag-Suppe (HTML-Quirks-Modus), E-Mail-Spam (SMTP-Missbrauch) und dem regelmäßigen Internet-Blackholing zufälliger Länder (schlechte BGP-Pushs) hängen. Wenn jemand gegen den Standard verstößt und es keinen historischen Grund gibt, dies zuzulassen, sollten Sie scheitern. Laut.

    – Kevin

    13. April 2018 um 17:37 Uhr

  • @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.

    Benutzer743382

    13. April 2018 um 22:18 Uhr

Benutzeravatar von Stargateur
Sternengatter

Frühe Vorschläge verlangten, dass der an main() übergebene Wert von argc “eins oder größer” sei. Dies wurde durch die gleiche Anforderung in Entwürfen der ISO-C-Norm vorangetrieben. Tatsächlich haben historische Implementierungen einen Wert von Null übergeben, wenn keine Argumente an den Aufrufer der exec-Funktionen geliefert wurden. Diese Anforderung wurde aus dem ISO-C-Standard und anschließend auch aus diesem Band von POSIX.1-2017 entfernt. Die Formulierung, insbesondere die Verwendung des Wortes should, erfordert, dass eine streng konforme POSIX-Anwendung mindestens ein Argument an die exec-Funktion weitergibt, wodurch sichergestellt wird, dass argc eins oder größer ist, wenn sie von einer solchen Anwendung aufgerufen wird. Tatsächlich ist dies eine bewährte Vorgehensweise, da viele vorhandene Anwendungen auf argv verweisen[0] ohne vorher den Wert von argc zu prüfen.

Die Anforderung an eine streng konforme POSIX-Anwendung besagt auch, dass der als erstes Argument übergebene Wert eine Dateinamenzeichenfolge ist, die dem gestarteten Prozess zugeordnet ist. Obwohl einige existierende Anwendungen unter bestimmten Umständen eher einen Pfadnamen als einen Dateinamen-String übergeben, ist ein Dateinamen-String allgemein nützlicher, da die übliche Verwendung von argv[0] ist in der Druckdiagnose. 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, was dem aufgerufenen Befehlsinterpreter anzeigt, dass es sich um eine “Anmelde-Shell” handelt.

Beachten Sie auch, dass der Test und [ utilities require specific strings for
the argv[0] Argument für deterministisches Verhalten über alle Implementierungen hinweg.

Quelle


Kann argc auf einem POSIX-System Null sein?

Ja, aber es wäre nicht strikt POSIX-konform.

wann immer Sie eine ausführbare Datei wie ausführen möchten ./a.out es wird ein Argument haben, das ist das program name. Aber es ist möglich, ein Programm mit auszuführen argc wie zero in Linux, indem Sie es von einem anderen aufrufenden Programm ausführen execv mit einem 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;
}

Benutzeravatar von Nominal Animal
Nominelles Tier

TL;DR: Ja, argv[0] kann NULL sein, aber aus keinem guten/vernünftigen Grund, den ich kenne. Es gibt jedoch Gründe, sich nicht darum zu kümmern argv[0] NULL ist, und um ausdrücklich zuzulassen, dass der Prozess abstürzt, wenn dies der Fall ist.


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

Die interessantere praktische Frage ist, sollte Ihr Programm kümmern.

Die Antwort darauf ist “Nein, Ihr Programm kann davon ausgehen argv[0] ist nicht NULL”da einige Systemdienstprogramme (Befehlszeilendienstprogramme) entweder nicht oder nicht deterministisch funktionieren, wenn argv[0] == NULL, aber was noch wichtiger ist, es gibt keinen guten Grund (außer Dummheit oder schändliche Zwecke), warum ein Prozess dies tun würde. (Ich bin mir nicht sicher, ob die Standardverwendung von getopt() schlägt dann auch fehl – aber ich würde nicht erwarten, dass es funktioniert.)

Viele Codes und tatsächlich 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 das ist angemessen und akzeptabel, da es keinen guten Grund für einen Prozess gibt, einen anderen Prozess auszuführen, ohne zumindest den auszuführenden Befehlspfad bereitzustellen, d. h execlp(cmd, cmd, NULL) statt execlp(cmd, NULL).

(Ich kann mir jedoch ein paar 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 selbst durch einen autorisierten Opferbefehl (run ohne Argumente, um eine minimale Startzeit zu gewährleisten), so dass der Dienst, der die Anfrage erhält, die Peer-Anmeldeinformationen überprüft, den Befehl des Opfers sieht und nicht den ursprünglichen bösen Prozess. Meiner Meinung nach ist dies das Beste für ein solches Opfer Befehle hart und schnell abstürzen (SIGSEGVdurch Dereferenzieren eines NULL-Zeigers), anstatt zu versuchen, sich “nett” zu verhalten und dem bösen Prozess ein größeres Zeitfenster zu geben.)

Mit anderen Worten, während es ist möglich für einen Prozess, sich selbst durch einen anderen zu ersetzen, aber ohne irgendwelche Argumente zu verursachen argc Null zu sein, ist ein solches Verhalten unvernünftig, in dem strengen Sinne, dass es keinen bekannten nicht schändlichen Grund dafür gibt.

Aus diesem Grund und aufgrund der Tatsache, dass ich es liebe, ruchlosen und rücksichtslosen Programmierern und ihren Programmen das Leben schwer zu machen, werde ich persönlich niemals die triviale Überprüfung 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 */

außer wenn dies von jemandem verlangt wird, der einen bestimmten bestehenden Anwendungsfall im Auge hat. Und selbst dann wäre ich etwas misstrauisch, da ich den Anwendungsfall erst einmal selbst verifizieren möchte.

1419330cookie-checkKann argc auf einem POSIX-System Null sein?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy