Was ist der Typ des Befehlszeilenarguments “argv” in C?
Lesezeit: 10 Minuten
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
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.
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
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
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.
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
autistisch
Genau genommen gibt es eine Reihe von Eigenschaften, die dafür vorhanden sein müssen argv ein Array sein. Betrachten wir einige davon:
#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
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
11871000cookie-checkWas ist der Typ des Befehlszeilenarguments “argv” in C?yes
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 verursachtThe 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