Was ist der Typ des Befehlszeilenarguments “argv” in C?

Lesezeit: 10 Minuten

Benutzer-Avatar
Jin

Ich lese einen Abschnitt aus C Primer Plus über Befehlszeilenargumente argv und ich habe Schwierigkeiten, diesen Satz zu verstehen.

Es steht dass,

Das Programm speichert die Befehlszeilenzeichenfolgen im Speicher und speichert die Adresse jeder Zeichenfolge in einem Array von Zeigern. Die Adresse dieses Arrays wird im zweiten Argument gespeichert. Per Konvention wird dieser Zeiger auf Zeiger aufgerufen argvfür Argumentwerte .

Bedeutet dies, dass die Befehlszeilenzeichenfolgen im Speicher als ein Array von Zeigern auf ein Array von gespeichert werden char?

  • Does this mean that the command line strings are stored in memory as an array of pointers to array of char? Ja. IMHO wird die ganze Verwirrung dadurch verursacht The program stores the command line strings in memory ... ; der Punkt ist, dass all dies geschieht bevor main() aufgerufen wird. Main() ist nur eine Funktion, die mit zwei Argumenten aufgerufen wird: einem int und einem Zeiger auf ein Array von String-Zeigern.

    – joop

    23. August 2016 um 9:39 Uhr

  • @joop argv ist kein “Zeiger auf ein Array von String-Zeigern”, wenn wir pedantisch sind. Diese ganze Frage dreht sich wirklich um den Unterschied zwischen “Zeiger auf ein Array” und “Zeiger auf das erste Element eines Arrays”.

    – MM

    23. August 2016 um 11:04 Uhr


  • Die Verwirrung der OPs besteht IMHO in Bezug auf die externe Seite (“crt0”), die die Argumente einrichtet, und die interne Seite (main()), die sie empfängt. Das ist auch die Ursache für den Unterschied zwischen den (gefühlten: verfallenen) Typen. Wirklich.

    – joop

    23. August 2016 um 11:13 Uhr

  • @joop dies ist eine “Language Lawyer” -Frage, was bedeutet, dass es um Standard C geht, in dem es kein “crt0” gibt und die Einrichtung der Argumente keine Rolle spielt, solange argv verhält sich wie im C-Standard spezifiziert

    – MM

    23. August 2016 um 11:25 Uhr

  • Das Tag “Sprachanwalt” wurde später hinzugefügt (von jemandem, der das nicht verstand Natur der Frage, IMHO) Und ich habe “crt0” aus einem bestimmten Grund zitiert. Wirklich.

    – joop

    23. August 2016 um 11:29 Uhr

Benutzer-Avatar
hackt

argv ist vom Typ char **. Es ist kein Array. Es ist ein Zeiger auf einen Zeiger char. Befehlszeilenargumente werden im Speicher gespeichert und die Adresse jeder Speicherstelle wird in einem Array gespeichert. Dieses Array ist ein Array von Zeigern auf char. argv zeigt auf das erste Element dieses Arrays.

                  Some
                  array

                 +-------+        +------+------+-------------+------+
argv ----------> |       |        |      |      |             |      |
                 | 0x100 +------> |      |      | . . . . . . |      |  Program Name1
         0x900   |       |        |      |      |             |      |
                 |       |        +------+------+-------------+------+
                 +-------+         0x100  0x101
                 |       |        +------+------+-------------+------+
                 | 0x205 |        |      |      |             |      |
         0x904   |       +------> |      |      | . . . . . . |      |  Arg1
                 |       |  .     |      |      |             |      |
                 +-------+        +------+------+-------------+------+
                 |  .    |  .      0x205  0x206
                 |  .    |
                 |  .    |  .
                 |  .    |
                 +-------+  .     +------+------+-------------+------+
                 |       |        |      |      |             |      |
                 | 0x501 +------> |      |      | . . . . . . |      |  Argargc-1
                 |       |        |      |      |             |      |
                 +-------+        +------+------+-------------+------+
                 |       |         0x501  0x502
                 | NULL  |
                 |       |
                 +-------+


0xXXX Represents memory address


1. In den meisten Fällen argv[0] stellt den Programmnamen dar, aber wenn der Programmname nicht aus der Hostumgebung verfügbar ist, dann argv[0][0] stellt ein Nullzeichen dar.

  • Ist die Adresse des gesamten Strings (&”string”) im Array gespeichert?

    – Jin

    23. August 2016 um 8:28 Uhr

  • @SouravGhosh; OK. Letzten Endes argv zeigt auf das erste Element eines Arrays von char * und das kann der Grund sein, warum der Standard es in diesem speziellen Fall als Array bezeichnet hat. Aber die Art von argv ist char **.

    – Hacken

    23. August 2016 um 8:37 Uhr

  • Wirklich hilfreiches ASCII-Schema!

    – Tim

    23. August 2016 um 9:41 Uhr

  • Als Nitpick sollte “irgendein Array” ein weiteres Element haben, das einen Nullzeiger speichert.

    – jamesdlin

    23. August 2016 um 12:44 Uhr

  • Auch bemerkenswert: argv[0] enthält nicht garantiert den Programmnamen (aber meistens), sondern nur etwas repräsentiert der Programmname: stackoverflow.com/q/2050961/1116364

    – Daniel Jour

    23. August 2016 um 13:39 Uhr

Benutzer-Avatar
Sourav Ghosh

Direkt zitieren aus C11Kapitel §5.1.2.2.1/p2, Programmstart, (Hervorhebung von mir)

int main(int argc, char *argv[]) { /* ... */ }

[…] Wenn der Wert von argc größer als Null ist, die Array-Mitglieder argv[0] durch
argv[argc-1] inclusive soll Zeiger auf Strings enthalten
, […]

und

[…] und die Zeichenfolgen, auf die durch die hingewiesen wird argv Reihe […]

Also im Prinzip, argv ist ein Zeiger auf das erste Element eines String-Arrays Hinweis. Deutlicher wird dies aus der Alternativform,

int main(int argc, char **argv) { /* ... */ }

Sie können dies als Zeiger auf das erste Element eines Arrays von Zeigern auf das erste Element von nullterminierten umformulieren char Arrays, aber ich würde lieber bei Strings bleiben.


HINWEIS:

Um die Verwendung von zu verdeutlichen “Zeiger auf das erste Element eines Arrays” in obiger Antwort nach §6.3.2.1/p3

Außer wenn es der Operand von ist sizeof Betreiber, der _Alignof Operator oder das Unäre & Operator oder ist ein Zeichenfolgenliteral, das zum Initialisieren eines Arrays verwendet wird, ein Ausdruck, der einen Typ hat ”Array des Typs” wird in einen Ausdruck mit Typ umgewandelt ”Zeiger auf Typ” das auf das Anfangselement des Array-Objekts zeigt und ist kein lvalue. […]

  • Wird dann die Adresse des gesamten Strings (&”string”) im Array gespeichert? Oder wird die Adresse des Anfangselements der Zeichenfolge im Array gespeichert?

    – Jin

    23. August 2016 um 8:49 Uhr

  • @Jin Adresse des Anfangselements der Zeichenfolge.

    – Sourav Ghosh

    23. August 2016 um 8:50 Uhr

  • …und da das Array an eine Funktion übergeben wird (main) es kollabiert zu einem Zeiger. So argv ist ein Zeiger, der auf ein Array zeigt. Versuchen sizeof argv und argv++ und das wirst du sehen argv ist ein Zeiger.

    – Klas Lindbäck

    23. August 2016 um 8:52 Uhr

  • Warum sagen Sie dann, dass “… als Array von Zeigern auf nullterminiert char Arrays”, nicht “…als Array von Zeigern auf nullterminierte char“? Ich denke, es gibt einen Unterschied zwischen diesen beiden Sätzen.. oder irre ich mich?

    – Jin

    23. August 2016 um 8:52 Uhr

  • @Sourac Ghosh Tut mir leid! Ich dachte “Zeiger auf eine nullterminierte char arrays” bedeutet Zeiger auf den gesamten String(&”String”)

    – Jin

    23. August 2016 um 8:57 Uhr


Dieser Thread ist so ein Zugunglück. Hier ist die Situation:

  • Es gibt ein Array mit argc+1 Elemente des Typs char *.
  • argv zeigt auf das erste Element dieses Arrays.
  • Es gibt argc andere Arrays des Typs char und verschiedene Längen, die nullterminierte Zeichenfolgen enthalten, die die Befehlszeilenargumente darstellen.
  • Die Elemente des Arrays von Zeigern zeigen jeweils auf das erste Zeichen eines der Arrays von char; mit Ausnahme des letzten Elements des Zeigerarrays, das ein Nullzeiger ist.

Manchmal schreiben Leute “Zeiger auf Array von X”, um “Zeiger auf das erste Element eines Arrays von X” zu bedeuten. Sie müssen die Kontexte und Typen verwenden, um herauszufinden, ob sie das tatsächlich gemeint haben oder nicht.

  • Irgendwo muss es zu dieser Frage ein genaues Duplikat geben.

    – Peter Mortensen

    23. August 2016 um 19:45 Uhr

Benutzer-Avatar
blau112

Ja genau.

argv ist ein char** oder char*[]oder einfach ein Array von char*-Zeigern.

Also arg[0] ist ein char* (eine Zeichenfolge) und argv[0][0] ist ein char.

Benutzer-Avatar
Peter K

Ja.

Die Art von argv ist char**dh ein Zeiger auf Zeiger auf char. Grundsätzlich gilt, wenn Sie a char* um eine Zeichenfolge zu sein argv ist ein Zeiger auf ein Array von Strings.

  • Die Strings müssen sich nicht in einem Array befinden, die Zeiger darauf aber schon. Außerdem ist das Ende des Arrays von Zeigern eine Null.

    – Marichyasana

    23. August 2016 um 8:21 Uhr

  • die Art von argv ist char**dh ein Zeiger auf ein Array von Zeigern auf char-Arrays: Auf keinen Fall.

    – Hacken

    23. August 2016 um 9:01 Uhr

  • @hackks Möchtest du näher darauf eingehen?

    – PeterK

    23. August 2016 um 9:02 Uhr

  • char ** wird gelesen als Zeiger auf Zeiger auf char und nicht Zeiger auf ein Array von Zeigern auf Arrays von char. Das macht argv ist vom Typ char *((*)[])[].

    – Hacken

    23. August 2016 um 9:05 Uhr


  • @hackks Mein Problem. Ich hatte gehofft, es intuitiver zu machen, aber es war einfach falsch.

    – PeterK

    23. August 2016 um 9:10 Uhr

Benutzer-Avatar
autistisch

Genau genommen gibt es eine Reihe von Eigenschaften, die dafür vorhanden sein müssen argv ein Array sein. Betrachten wir einige davon:

¹/ Es darf kein Array geben, auf das ein Nullzeiger zeigt, as Nullzeiger sind garantiert eine Adresse, die sich von der eines beliebigen Objekts unterscheidet. Deswegen, argv im folgenden Code kann kein Array sein:

#include <assert.h>
int main(int argc, char *argv[]) {
    if (argv) return main(0, 0);
    assert(argv == 0); // argv is a null pointer, not to be dereferenced
}

²/ Es ist ungültig, es einem Array zuzuweisen. Zum Beispiel, char *argv[] = { 0 }; argv++; ist eine Beschränkungsverletzung, aber int main(int argc, char *argv[]) { argv++; } kompiliert und läuft gut. Also müssen wir aus diesem Punkt schließen, dass argv ist kein Array, wenn es als Argument deklariert wird, sondern ein Zeiger, der (möglicherweise) auf ein Array zeigt. (Dies ist eigentlich dasselbe wie Punkt 1, kommt aber aus einem anderen Blickwinkel als Calling main mit einem Nullzeiger als argv ist tatsächlich eine Neuzuweisung argvetwas, das wir mit Arrays nicht machen können).

³/ … Wie die C-Norm sagt:

Eine andere Verwendung des sizeof-Operators ist die Berechnung der Anzahl der Elemente in einem Array: sizeof-Array / sizeof-Array[0]

Zum Beispiel:

#include <assert.h>
int main(int argc, char *argv[]) {
    size_t size = argc+1; // including the NULL
    char *control[size];
    assert(sizeof control / sizeof *control == size); // this test passes, because control is actually an array
    assert(sizeof argv   / sizeof *argv    == size); // this test fails for all values of size != 1, indicating that argv isn't an array
}

⁴/ Der unäre &address-of-Operator ist so definiert, dass er bei Anwendung auf ein Array den gleichen Wert eines anderen Typs ergibt, also zum Beispiel:

#include <assert.h>
int main(int argc, char *argv[]) {
    char *control[42];
    assert((void *) control == (void *) &control); // this test passes, because control is actually an array
    assert((void *) argv    == (void *) &argv); // this test fails, indicating that argv isn't an array
}

  • Die Strings müssen sich nicht in einem Array befinden, die Zeiger darauf aber schon. Außerdem ist das Ende des Arrays von Zeigern eine Null.

    – Marichyasana

    23. August 2016 um 8:21 Uhr

  • die Art von argv ist char**dh ein Zeiger auf ein Array von Zeigern auf char-Arrays: Auf keinen Fall.

    – Hacken

    23. August 2016 um 9:01 Uhr

  • @hackks Möchtest du näher darauf eingehen?

    – PeterK

    23. August 2016 um 9:02 Uhr

  • char ** wird gelesen als Zeiger auf Zeiger auf char und nicht Zeiger auf ein Array von Zeigern auf Arrays von char. Das macht argv ist vom Typ char *((*)[])[].

    – Hacken

    23. August 2016 um 9:05 Uhr


  • @hackks Mein Problem. Ich hatte gehofft, es intuitiver zu machen, aber es war einfach falsch.

    – PeterK

    23. August 2016 um 9:10 Uhr

Benutzer-Avatar
FrageC

argv ist ein Array von Zeigern auf Zeichen.

Der folgende Code zeigt den Wert von an argvDie Inhalte von argv und führt einen Speicherauszug auf dem Speicher durch, auf den durch den Inhalt von gezeigt wird argv. Hoffentlich erhellt dies die Bedeutung der Indirektion.

#include <stdio.h>
#include <stdarg.h>

print_memory(char * print_me)
{
    char * p;
    for (p = print_me; *p != '\0'; ++p)
    {
        printf ("%p: %c\n", p, *p);
    }

    // Print the '\0' for good measure
    printf ("%p: %c\n", p, *p);

}

int main (int argc, char ** argv) {
    int i;

    // Print argv
    printf ("argv: %p\n", argv);
    printf ("\n");

    // Print the values of argv
    for (i = 0; i < argc; ++i)
    {
        printf ("argv[%d]: %p\n", i, argv[i]);
    }
    // Print the NULL for good measure
    printf ("argv[%d]: %p\n", i, argv[i]);
    printf ("\n");

    // Print the values of the memory pointed at by argv
    for (i = 0; i < argc; ++i)
    {
        print_memory(argv[i]);
    }

    return 0;
}

Beispiellauf:

$ ./a.out Hello World!
argv: ffbfefd4

argv[0]: ffbff12c
argv[1]: ffbff134
argv[2]: ffbff13a
argv[3]: 0

ffbff12c: .
ffbff12d: /
ffbff12e: a
ffbff12f: .
ffbff130: o
ffbff131: u
ffbff132: t
ffbff133:
ffbff134: H
ffbff135: e
ffbff136: l
ffbff137: l
ffbff138: o
ffbff139:
ffbff13a: W
ffbff13b: o
ffbff13c: r
ffbff13d: l
ffbff13e: d
ffbff13f: !
ffbff140:

$

Sie haben dieses große zusammenhängende Array, das von reicht ffbff12c zu ffbff140 die die Befehlszeilenargumente enthält (dies ist nicht garantiert zusammenhängend durch den Standard, aber so wird es im Allgemeinen gemacht). argv enthält nur Zeiger auf dieses Array, damit Sie wissen, wo Sie nach den Wörtern suchen müssen.

argv ist ein Zeiger … auf Zeiger … auf Zeichen

  • Es ist nicht im C- oder POSIX-Standard, aber es kann garantiert werden, dass es durch den System V ABI-Standard zusammenhängend ist.

    – Random832

    23. August 2016 um 16:30 Uhr

  • Seite 34 von software.intel.com/sites/default/files/article/402129/… beschreibt die Details dessen, was für dieses Array erforderlich ist und was nicht: “Argument-Strings, Umgebungs-Strings und die Hilfsinformationen erscheinen in keiner bestimmten Reihenfolge innerhalb des Informationsblocks und müssen nicht kompakt zugewiesen werden.” [but the “information block” itself is a well-defined area at the top of memory that is defined to contain all the strings]. Offensichtlich ist dies nur für Systeme relevant, für die diese Norm gilt.

    – Random832

    23. August 2016 um 16:37 Uhr


1187100cookie-checkWas ist der Typ des Befehlszeilenarguments “argv” in C?

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

Privacy policy