C erhält die Hauptargumente aus der Funktion. Hauptfunktionsargumente. Verwenden von Befehlszeilenargumenten


Manchmal ist es beim Starten eines Programms nützlich, ihm einige Informationen zu übergeben. Normalerweise werden diese Informationen über Befehlszeilenargumente an main() übergeben. Befehlszeilenargument sind Informationen, die in der Befehlszeile des Betriebssystems nach dem Programmnamen eingegeben werden. Um beispielsweise mit dem Kompilieren eines Programms zu beginnen, müssen Sie nach der Eingabeaufforderung in der Befehlszeile etwa Folgendes eingeben:

CC Programmname

Programmname ist ein Befehlszeilenargument; es gibt den Namen des Programms an, das Sie kompilieren möchten.

Um Befehlszeilenargumente zu akzeptieren, werden zwei spezielle integrierte Argumente verwendet: argc und argv. Der argc-Parameter enthält die Anzahl der Argumente in der Befehlszeile und ist eine Ganzzahl, und er ist immer mindestens 1, da das erste Argument der Name des Programms ist. Und der Parameter argv ist ein Zeiger auf ein Array von Zeigern auf Zeichenfolgen. In diesem Array zeigt jedes Element auf ein Befehlszeilenargument. Alle Befehlszeilenargumente sind Strings, daher muss bei der Programmentwicklung für die Konvertierung beliebiger Zahlen in das gewünschte Binärformat gesorgt werden.

Hier ist ein einfaches Beispiel für die Verwendung eines Befehlszeilenarguments. Auf dem Bildschirm werden das Wort Hallo und Ihr Name angezeigt, der als Befehlszeilenargument angegeben werden muss.

#enthalten #enthalten int main(int argc, char *argv) ( if(argc!=2) ( printf("Sie haben vergessen, Ihren Namen einzugeben.\n"); exit(1); ) printf("Hallo %s", argv) ; 0 zurückgeben; )

Wenn Sie diesen Programmnamen (Name) aufgerufen haben und Ihr Name Tom ist, müssen Sie zum Ausführen des Programms den Namen Tom in die Befehlszeile eingeben. Als Ergebnis der Ausführung des Programms erscheint die Meldung Hallo, Tom auf dem Bildschirm.

In vielen Umgebungen müssen alle Befehlszeilenargumente durch ein Leerzeichen oder Tabulatorzeichen getrennt werden. Kommas, Semikolons und ähnliche Zeichen gelten nicht als Trennzeichen. Zum Beispiel,

Lauf Spot, lauf

besteht aus drei Zeichenfolgen, while

Eric, Rick, Fred

stellt eine einzelne Zeichenfolge dar – Kommas gelten in der Regel nicht als Trennzeichen.

Wenn eine Zeichenfolge Leerzeichen enthält, können Sie die Zeichenfolge in einigen Umgebungen in doppelte Anführungszeichen setzen, um zu verhindern, dass mehrere Argumente generiert werden. Infolgedessen wird die gesamte Zeichenfolge als ein Argument betrachtet. Um mehr darüber zu erfahren, wie Ihr Betriebssystem Befehlszeilenparameter festlegt, lesen Sie die Dokumentation Ihres Betriebssystems.

Es ist sehr wichtig, argv korrekt zu deklarieren. So wird es am häufigsten gemacht:

Char *argv;

Leere eckige Klammern zeigen an, dass das Array eine unbestimmte Länge hat. Auf einzelne Argumente kann jetzt durch Indizieren des argv-Arrays zugegriffen werden. argv zeigt beispielsweise auf die erste Zeichenfolge, die immer der Programmname ist; argv zeigt auf das erste Argument und so weiter.

Ein weiteres kleines Beispiel für die Verwendung von Befehlszeilenargumenten ist das folgende Programm: Countdown. Dieses Programm zählt rückwärts, beginnend mit einem Wert (in der Befehlszeile angegeben) und gibt einen Signalton aus, wenn er 0 erreicht. Beachten Sie, dass das erste Argument, das den Startwert enthält, mithilfe der Standardfunktion atoi () in einen ganzzahligen Wert konvertiert wird. Wenn das zweite Befehlszeilenargument (und wenn wir den Programmnamen als Argument betrachten, dann das dritte) die Zeile „display“ (display) ist, wird das Ergebnis der Zählung (in umgekehrter Reihenfolge) auf dem Bildschirm angezeigt.

/* Programm zum Zählen in umgekehrter Reihenfolge. */ #enthalten #enthalten #enthalten #enthalten int main(int argc, char *argv) ( int disp, count; if(argc<2) { printf("В командной строке необходимо ввести число, с которого\n"); printf("начинается отсчет. Попробуйте снова.\n"); exit(1); } if(argc==3 && !strcmp(argv, "display")) disp = 1; else disp = 0; for(count=atoi(argv); count; --count) if(disp) printf("%d\n", count); putchar("\a"); /* здесь подается звуковой сигнал */ printf("Счет закончен"); return 0; }

Bitte beachten Sie, dass eine Fehlermeldung angezeigt wird, wenn keine Befehlszeilenargumente angegeben werden. Programme, die Befehlszeilenargumente akzeptieren, führen häufig Folgendes aus: Wenn der Benutzer diese Programme ausführt, ohne die erforderlichen Informationen einzugeben, werden Anweisungen zur korrekten Angabe der Argumente angezeigt.

Um auf ein einzelnes Zeichen eines der Befehlszeilenargumente zuzugreifen, geben Sie einen zweiten Index in argv ein. Das folgende Programm gibt beispielsweise zeichenweise alle Argumente aus, mit denen es aufgerufen wurde:

#enthalten int main(int argc, char *argv) ( int t, i; for(t=0; t

Denken Sie daran, dass der erste Index von argv den Zugriff auf die Zeichenfolge und der zweite Index den Zugriff auf die einzelnen Zeichen ermöglicht.

Normalerweise werden argc und argv verwendet, um dem Programm die ersten Befehle zu geben, die es beim Start benötigt. Befehlszeilenargumente geben beispielsweise häufig Informationen wie einen Dateinamen, eine Option oder ein alternatives Verhalten an. Die Verwendung von Befehlszeilenargumenten verleiht Ihrem Programm ein „professionelles Aussehen“ und erleichtert die Verwendung in Batchdateien.

Die Namen argc und argv sind traditionell, aber nicht erforderlich. Sie können diese beiden Parameter in der Funktion main() beliebig aufrufen. Darüber hinaus unterstützen einige Compiler möglicherweise zusätzliche Argumente für main(). Überprüfen Sie daher unbedingt die Dokumentation Ihres Compilers.

Wenn ein Programm keine Befehlszeilenparameter benötigt, ist es am häufigsten, die Funktion main() explizit so zu deklarieren, dass sie keine Parameter hat. In diesem Fall wird das Schlüsselwort void in der Parameterliste dieser Funktion verwendet.

Stichworte: Befehlszeilenoptionen

Befehlszeilenoptionen

C ist eine kompilierte Sprache. Nach der Assemblierung ist das Programm eine ausführbare Datei (wir erwägen nicht die Erstellung dynamischer Bibliotheken, Treiber usw.). Unsere Programme sind sehr einfach und enthalten keine Laufzeitbibliotheken, sodass sie auf einen Computer mit demselben Betriebssystem (und ähnlicher Architektur) übertragen und dort ausgeführt werden können.

Das Programm kann beim Start Parameter übernehmen. Sie sind Argumente für die Hauptfunktion. Die allgemeine Ansicht der Hauptfunktion ist wie folgt

Void main(int argc, char **argv) ( ... )

Das erste Argument von argc ist die Anzahl der an die Funktion übergebenen Parameter. Das zweite Argument ist ein Array aus Strings – den Parametern selbst. Da die Parameter einer Funktion alles sein können, werden sie als Zeichenfolgen übergeben und das Programm selbst muss sie analysieren und in den gewünschten Typ konvertieren.

Das erste Argument (argv) ist immer der Programmname. In diesem Fall wird der Name abhängig davon angezeigt, von wo aus das Programm gestartet wurde.

#enthalten #enthalten void main(int argc, char **argv) ( printf("%s", argv); )

Lassen Sie uns nun lernen, ein wenig mit der Befehlszeile zu arbeiten. Dies wird benötigt, um Argumente an unser Programm zu übergeben. Die Tastenkombination Win+R öffnet das Fenster „Ausführen“. Geben Sie cmd ein und Sie öffnen die Befehlszeile. Sie können cmd.exe auch finden, indem Sie im Startmenü suchen. In Unix-ähnlichen Betriebssystemen können Sie das Terminalprogramm aufrufen.

Wir werden nicht zu viele Befehle lernen. Nur diejenigen, die für die Arbeit benötigt werden.

Der für alle Betriebssysteme standardmäßige Befehl cd navigiert zum gewünschten Ordner. Es gibt zwei reservierte Namen – . (Punkt) und.. (zwei Punkte). Der Punkt ist der Name des aktuellen Ordners.

Geht nirgendwo hin

Zugriff auf den übergeordneten Ordner

Gehen Sie zum übergeordneten Ordner

Um zur gewünschten zu gelangen, geben Sie die CD-Adresse ein. Beispielsweise müssen Sie in Windows zum Ordner C:\Windows\System32 wechseln

Cd C:\Windows\System32

Unter Linux gehen Sie bei Bedarf zum Ordner /var/mysql

Cd /var/mysql

Wenn der Pfad Leerzeichen enthält, wird er in doppelte Anführungszeichen geschrieben

Cd „D:\Dokumente und Einstellungen\Prolog“

Das Terminal verfügt über die folgenden nützlichen Funktionen: Wenn Sie den Aufwärtspfeil drücken, wird der zuvor ausgeführte Befehl angezeigt. Wenn Sie die Tabulatortaste drücken, versucht das Terminal, die Zeile mit einem ihm bekannten Befehl zu vervollständigen oder den Pfad zu vervollständigen, indem es alle Ordner und Dateien im aktuellen Ordner durchläuft.
Geben Sie cd C:\ ein.
Drücken Sie die Tabulatortaste und sehen Sie, was passiert.

Ein weiterer wichtiger Befehl, dir unter Windows und ls unter Linux, zeigt den Inhalt des aktuellen Ordners (des Ordners, in dem Sie sich gerade befinden) in der Konsole an.

Ihr Programm hat seinen vollständigen Namen zurückgegeben. Gehen Sie zu dem Ordner, in dem sich Ihr Programm befindet, und sehen Sie sich dessen Inhalt an


Nachdem wir nun zu unserem Ordner navigiert sind, können wir unser Programm ausführen. Geben Sie dazu ihren Namen ein.


Beachten Sie, dass sich der Name geändert hat. Da das Programm aus einem eigenen Ordner aufgerufen wird, wird der relative Name angezeigt. Jetzt ändern wir das Programm und lassen es alle Argumente ausgeben. die ihr geschenkt wurden.

#enthalten #enthalten void main(int argc, char **argv) ( int i; for (i = 0; i< argc; i++) { printf("%s\n", argv[i]); } }

Stellen Sie das Projekt zusammen. Stellen Sie vor dem Zusammenbau sicher, dass das Programm geschlossen ist. Rufen Sie nun das Programm auf und übergeben Sie ihm verschiedene Argumente. Schreiben Sie dazu den Namen des Programms und die durch ein Leerzeichen getrennten Argumente


Schreiben wir nun ein Programm, das zwei Zahlenargumente akzeptiert und deren Summe ausgibt

#enthalten #enthalten #enthalten void main(int argc, char **argv) ( int a, b; if (argc != 3) ( printf("Fehler: %d Argumente gefunden. Benötigt genau 2", argc-1); exit(1); ) a = atoi(argv); b = atoi(argv); printf("%d", a + b); )

Lasst uns abholen und anrufen


So funktionieren die meisten Programme. Durch Klicken auf eine Verknüpfung rufen Sie das Programm auf, auf das sie verweist. Die meisten Programme akzeptieren auch verschiedene Argumente. Sie können beispielsweise den Firefox-Browser über die Befehlszeile aufrufen und die Argumente übergeben
firefox.exe „www.mozilla.org“ „site“ und öffnet sofort die Websites an den angegebenen Adressen in zwei Registerkarten.

Viele Standardbefehle verfügen auch über Parameter. In Windows ist es üblich, dass sie mit einem Schrägstrich beginnen, in Unix mit einem Minus oder zwei Minuszeichen. Zum Beispiel

Zeigt nur Ordner an, jedoch im Linux-Terminal

Ls -l listet alle Dateien und Ordner mit Attributen auf

Um zusätzliche Windows-Befehle anzuzeigen, geben Sie „help“ in die Befehlszeile ein oder lesen Sie im Handbuch nach (es ist leicht im Internet zu finden). Für Linux gibt es viel mehr Befehle und ihre Optionen, und einige davon sind unabhängige Programmiersprachen, daher lohnt es sich, zumindest den Mindestsatz und ihre Optionen zu lernen.

Borland C++ unterstützt drei Argumente für main(). Die ersten beiden sind das traditionelle argc und argv. Dies sind die einzigen durch den ANSI-C-Standard definierten Argumente für main(). Sie ermöglichen die Übergabe von Befehlszeilenargumenten an das Programm. Befehlszeilenargumente sind die Informationen, die dem Programmnamen in der Befehlszeile des Betriebssystems folgen. Wenn ein Programm beispielsweise mit dem Borland-Zeilencompiler kompiliert wird, wird es normalerweise als bcc eingegeben Programmname

Wo Programmname ist ein Programm, das kompiliert werden muss. Der Programmname wird als Argument an den Compiler übergeben.

Der Parameter argc enthält die Anzahl der Befehlszeilenargumente und ist eine Ganzzahl. Es ist immer mindestens 1, da der Programmname als erstes Argument gilt. Der argv-Parameter ist ein Zeiger auf ein Array von Zeichenzeigern. Jedes Element dieses Arrays zeigt auf ein Befehlszeilenargument. Alle Befehlszeilenargumente sind Zeichenfolgen. Alle Zahlen werden vom Programm in das interne Format umgewandelt. Das folgende Programm gibt „Hallo“ gefolgt vom Benutzernamen aus, wenn es direkt nach dem Programmnamen eingegeben wird:

#enthalten

{
if(argc!=2)
{
printf("Sie haben vergessen, Ihren Namen einzugeben\n");
Rückgabe 1;
}
printf("Hallo %s", argv);
0 zurückgeben;
}

Wenn Sie diesen Programmnamen nennen und der Benutzername Sergey ist, müssen Sie zum Starten des Programms Folgendes eingeben:
Name Sergej.
Als Ergebnis des Programms erscheint Folgendes:
„Hallo Sergey.“

Befehlszeilenargumente müssen durch Leerzeichen oder Tabulatoren getrennt werden. Kommas, Semikolons und ähnliche Zeichen gelten nicht als Trennzeichen. Zum Beispiel:

Besteht aus drei Zeilen, während

Herb, Rick, Fred

Dies ist eine Zeile – Kommas sind keine Trennzeichen.

Wenn Sie eine Zeichenfolge mit Leerzeichen oder Tabulatoren als einzelnes Argument übergeben müssen, müssen Sie diese in doppelte Anführungszeichen setzen. Dies ist zum Beispiel ein Argument:

"das ist ein Test"

Es ist wichtig, argv korrekt zu deklarieren. Die typischste Methode ist:

Leere Klammern zeigen an, dass das Array keine feste Länge hat. Sie können mithilfe der argv-Indizierung auf einzelne Elemente zugreifen. argv zeigt beispielsweise auf die erste Zeile, die immer den Programmnamen enthält. argv zeigt auf die nächste Zeile und so weiter.

Nachfolgend finden Sie ein kleines Beispiel für die Verwendung von Befehlszeilenargumenten. Es zählt ab dem in der Befehlszeile angegebenen Wert herunter und gibt ein Signal aus, wenn dieser Null erreicht. Beachten Sie, dass das erste Argument eine Zahl enthält, die mithilfe der Standardfunktion atoi() in eine Ganzzahl umgewandelt wurde. Wenn als zweites Argument die Zeichenfolge „display“ vorhanden ist, wird der Zähler selbst auf dem Bildschirm angezeigt.

/* Zählprogramm */

#enthalten
#enthalten
#enthalten
int main(int argc, char *argv)
{
int disp, count;
if(argc<2)
{
printf("Sie müssen die Länge der Zählung eingeben\n");
printf("in der Befehlszeile. Versuchen Sie es erneut.\n");
Rückgabe 1;
}
if (argc==3 && !strcmp(argv,"display")) disp = 1;
sonst disp = 0;
for(count=atoi(argv); count; -count)
if (disp) printf("%d ", count);
printf("%c", "\a"); /* auf den meisten Computern ist dies ein Anruf */
0 zurückgeben;
}

Bitte beachten Sie, dass eine Fehlermeldung erscheint, wenn keine Argumente angegeben werden. Dies ist am typischsten für Programme, die Befehlszeilenargumente verwenden, um Anweisungen auszugeben, wenn versucht wurde, das Programm ohne die richtigen Informationen auszuführen.

Um auf einzelne Befehlszeilenzeichen zuzugreifen, fügen Sie argv einen zweiten Index hinzu. Das folgende Programm gibt beispielsweise alle Argumente, mit denen es aufgerufen wurde, zeichenweise aus:

#enthalten
int main(int argc, char *argv)
{
int t, i;
for(t=0; t {
ich = 0;
while(argv[t][i])
{
printf("%c", argv[t][i]);
}
printf(" ");
}
0 zurückgeben;
}

Wir müssen bedenken, dass der erste Index für den Zugriff auf eine Zeichenfolge und der zweite für den Zugriff auf ein Zeichen in einer Zeichenfolge dient.

Normalerweise werden argc und argv verwendet, um Quellbefehle abzurufen. Theoretisch sind bis zu 32767 Argumente möglich, aber die meisten Betriebssysteme erlauben nicht einmal annähernd diese Zahl. Normalerweise werden diese Argumente verwendet, um einen Dateinamen oder Optionen anzugeben. Die Verwendung von Befehlszeilenargumenten verleiht dem Programm ein professionelles Aussehen und ermöglicht die Verwendung des Programms in Batchdateien.

Wenn Sie die mit Borland C++ gelieferte Datei WILDARGS.OBJ einbinden, können Sie Vorlagen in Argumenten vom Typ *.EXE verwenden. (Borland C++ verarbeitet Platzhalter automatisch und erhöht argc entsprechend.) Wenn Sie beispielsweise WILDARGS.OBJ mit dem folgenden Programm verbinden, erfahren Sie, wie viele Dateien mit dem in der Befehlszeile angegebenen Dateinamen übereinstimmen:

/* Dieses Programm mit WILDARGS.OBJ verknüpfen */

#enthalten
int main(int argc, char *argv)
{
registrieren int i;
printf("%d Dateien stimmen mit dem angegebenen Namen überein\n", argc-1);
printf("Sie sind: ");
for(i=1; i printf("%s", argv[i]);
0 zurückgeben;
}

Wenn wir dieses Programm WA nennen und es dann wie folgt ausführen, erhalten wir die Anzahl der Dateien mit der Erweiterung EXE und eine Liste der Namen dieser Dateien:

Zusätzlich zu argc und argv bietet Borland C++ auch ein drittes Befehlszeilenargument –env. Der env-Parameter ermöglicht einem Programm den Zugriff auf Informationen über die Betriebssystemumgebung. Der Parameter env muss auf argc und argv folgen und wird wie folgt deklariert:

Wie Sie sehen, wird env auf die gleiche Weise wie argv deklariert. Genau wie argv ist es ein Zeiger auf ein Array von Strings. Jede Zeile ist eine vom Betriebssystem definierte Umgebungszeichenfolge. Der env-Parameter hat keinen entsprechenden argc-Parameter, der angibt, wie viele Umgebungszeilen vorhanden sind. Stattdessen ist die letzte Zeile der Umgebung null. Das folgende Programm druckt alle derzeit im Betriebssystem definierten Umgebungszeichenfolgen:

/* Dieses Programm zeigt alle Umgebungszeilen an */

#enthalten
int main(int argc, char *argv, char *env)
{
int t;
for(t=0; env[t]/ t++)
printf("%s\n", env[t]);
0 zurückgeben;
}

Bitte beachten Sie, dass argc und argv zwar nicht vom Programm verwendet werden, aber in der Parameterliste vorhanden sein müssen. C kennt die Parameternamen nicht. Stattdessen wird ihre Verwendung durch die Reihenfolge bestimmt, in der die Parameter deklariert werden. Tatsächlich können Sie den Parameter beliebig nennen. Da argc, argv und env traditionelle Namen sind, ist es am besten, sie weiterhin zu verwenden, damit jeder, der das Programm liest, sofort verstehen kann, dass es sich um Argumente für die Funktion main() handelt.

Eine typische Aufgabe für Programme besteht darin, einen in einer Umgebungszeichenfolge definierten Wert nachzuschlagen. Beispielsweise ermöglicht der Inhalt der PATH-Zeile Programmen die Verwendung von Suchpfaden. Das folgende Programm zeigt, wie Sie Zeichenfolgen finden, die Standardsuchpfade deklarieren. Es verwendet die Standardbibliotheksfunktion strstr(), die den folgenden Prototyp hat:

Char *strstr(const char *str1, const char *str2);

Die Funktion strstr() sucht in der Zeichenfolge, auf die str2 zeigt, nach der Zeichenfolge, auf die str1 zeigt. Wird ein solcher String gefunden, wird ein Zeiger auf die erste Position zurückgegeben. Wenn keine Übereinstimmungen gefunden werden, gibt die Funktion NULL zurück.

/* Das Programm sucht in den Umgebungszeichenfolgen nach einer Zeile mit PATH */

#enthalten
#enthalten
int main (int argc, char *argv, char *env)
{
int t;
for(t=0; env[t]; t++)
{
if(strstr(env[t], "PATH"))
printf("%s\n", env[t]);
}
0 zurückgeben;
}

Es kommt vor, dass Daten beim Aufruf von der Kommandozeile an ein Programm übertragen werden. Solche Daten werden Befehlszeilenargumente genannt. Das sieht zum Beispiel so aus:

./a.out test.txt ls -lt /home/peter/

Hier werden die Programme a.out (aus dem aktuellen Verzeichnis) und ls (aus demselben Verzeichnis, das in der Umgebungsvariablen PATH angegeben ist) aufgerufen. Das erste Programm empfängt von der Befehlszeile ein Wort – test.txt, das zweite – zwei: -lt und /home/peter/.

Wenn das Programm in C geschrieben ist, wird die Steuerung beim Start sofort an die Funktion main() übergeben. Daher ist es die Funktion, die die Befehlszeilenargumente empfängt, die ihren Parametervariablen zugewiesen sind.

Zuvor haben wir die Funktion main() so definiert, als ob sie keine Parameter akzeptiert und nichts zurückgibt. Tatsächlich gibt in der Sprache C jede Funktion standardmäßig (sofern nichts anderes definiert ist) eine Ganzzahl zurück. Da können Sie sicher sein. Wenn Sie den Code so schreiben:

main() ( printf ("Hallo \N"); return 0 ; )

Dann tritt beim Kompilieren keine Warnung oder kein Fehler auf. Das Gleiche passiert, wenn Sie int main() schreiben. Dies beweist, dass die Funktion standardmäßig eine Ganzzahl und nicht nichts (void) zurückgibt. Obwohl das, was eine Funktion zurückgibt, immer „überschrieben“ werden kann, zum Beispiel voidmain() oder float main() .

Beim Aufruf eines Programms über die Befehlszeile wird ihm immer das folgende Datenpaar übergeben:

  1. ganze Zahl, Angabe der Anzahl der Wörter (durch Leerzeichen getrennte Elemente) in der Befehlszeile beim Aufruf,
  2. Zeiger auf ein Array von Strings, wobei jede Zeile ein separates Wort aus der Befehlszeile ist.

Bedenken Sie, dass auch der Programmname selbst zählt. Wenn der Anruf beispielsweise so aussieht:

./a.out 12 Thema 2

Dann hat das erste Argument des Programms den Wert 4 und das String-Array ist definiert als („./a.out“, „12“, „theme“, „2“).

Beachten Sie die Terminologie: Es gibt nur zwei Programmargumente (eine Zahl und ein Array), aber beliebig viele Befehlszeilenargumente. Befehlszeilenargumente werden in Programmargumente (in main()-Funktionsargumente) „umgewandelt“.
Diese Daten (Nummer und Zeiger) werden dem Programm auch dann übergeben, wenn es einfach namentlich aufgerufen wird, ohne ihm etwas zu übergeben: ./a.out. In diesem Fall hat das erste Argument den Wert 1 und das zweite zeigt auf ein Array, das nur aus einer Zeile besteht („"./a.out").

Nur weil Daten an das Programm übergeben werden, bedeutet das nicht, dass die Funktion main() diese akzeptieren muss. Wenn die Funktion main() ohne Parameter definiert ist, ist der Zugriff auf die Befehlszeilenargumente nicht möglich. Obwohl Sie nichts daran hindert, sie zu übertragen. Es wird kein Fehler auftreten.

Um auf die an das Programm übergebenen Daten zugreifen zu können, müssen diese Variablen zugewiesen werden. Da die Argumente sofort an main() übergeben werden, sollte der Header wie folgt aussehen:
main (int n, char *arr)

Die erste Variable (n) enthält die Anzahl der Wörter und die zweite Variable enthält einen Zeiger auf ein Array von Zeichenfolgen. Oft wird der zweite Parameter als **arr geschrieben. Es ist jedoch dasselbe. Denken Sie daran, dass das String-Array selbst Zeiger auf Strings als seine Elemente enthält. Und wir übergeben der Funktion einen Zeiger auf das erste Element des Arrays. Es stellt sich heraus, dass wir einen Zeiger auf einen Zeiger übergeben, d.h. **arr.

Übung
Schreiben Sie ein Programm wie dieses:

#enthalten int main(int argc, char ** argv) ( int i; printf ("%d \N", argc) ; für (i= 0 ; ich< argc; i++ ) puts (argv[ i] ) ; }

Es zeigt beim Aufruf die Anzahl der Wörter in der Befehlszeile und jedes Wort in einer neuen Zeile an. Rufen Sie es ohne Befehlszeilenargumente und mit Argumenten auf.

Im Programm haben wir die Parametervariablen argc und argv verwendet. Es ist üblich, diese Namen zu verwenden, aber tatsächlich können sie alles sein. Halten Sie sich besser an diesen Standard, damit Ihre Programme nicht nur für Sie, sondern auch für andere Programmierer besser verständlich sind.

Die praktische Bedeutung der Datenübertragung an das Programm

Wenn Sie Erfahrung mit der GNU/Linux-Befehlszeile haben, wissen Sie, dass die meisten Befehle Schalter und Argumente haben. Wenn Sie beispielsweise den Inhalt von Verzeichnissen anzeigen, kopieren oder verschieben, werden die Dateisystemobjekte, für die der Befehl ausgeführt wird, als Argumente angegeben. Die Merkmale seiner Implementierung werden anhand von Schlüsseln bestimmt. Zum Beispiel im Team

Cp -r ../les_1 ../les_101

cp ist der Befehlsname, -r ist der Schalter und ../les_1 und ../les_101 sind die Befehlsargumente.

Im Allgemeinen werden Dateiadressen und „Modifikatoren“ (das sind Schlüssel) des Programmausführungsprozesses meistens beim Start an Programme übertragen.

Schreiben wir ein Programm, das vom Benutzer in der Befehlszeile angegebene Dateien zum Schreiben oder Hinzufügen öffnet und dort die gleichen Informationen schreibt (hinzufügt), die der Benutzer während der Programmausführung über die Tastatur eingibt:

#enthalten #enthalten main (int argc, char ** argv) ( int i, ch; FILE * f[ 5 ] ; if (argc< 3 || argc >7) (setzt ( „Ungültige Anzahl Parameter“); return 1 ; ) if (strcmp (argv[ 1 ] , "-w" ) != 0 && strcmp (argv[ 1 ] , "-a" ) != 0 ) ( puts ( „Der erste Parameter kann entweder -w oder -a sein“); return 2 ; ) für (i= 0 ; i< argc- 2 ; i++ ) { f[ i] = fopen (argv[ i+ 2 ] , argv[ 1 ] + 1 ) ; if (f[ i] == NULL) { printf („Die Datei %s kann nicht geöffnet werden\n“, argv[ i+ 2 ] ); return 3 ; ) ) while ((ch = getchar () ) != EOF) für (i= 0 ; i< argc- 2 ; i++ ) putc (ch, f[ i] ) ; for (i= 0 ; i < argc- 2 ; i++ ) fclose (f[ i] ) ; return 0 ; }

Erläuterungen zum Code:

  1. Es wird ein Array mit fünf Dateizeigern erstellt. Daher können Sie nicht mehr als fünf Dateien gleichzeitig öffnen. Der Dateizeiger der ersten Datei wird im Array-Element f gespeichert, der zweite in f usw.
  2. Die Anzahl der Befehlszeilenargumente wird überprüft. Es sollten mindestens drei davon sein, denn... Der erste ist der Programmname, der zweite ist der Dateiöffnungsmodus, der dritte ist die erste oder einzige Datei, in die geschrieben werden soll. Da Sie mit dem Programm nur fünf Dateien öffnen können, darf die Gesamtzahl der Befehlszeilenargumente nicht mehr als sieben betragen. Wenn also die Anzahl der Argumente weniger als 3 oder mehr als 7 beträgt, endet das Programm, weil Die return-Anweisung bewirkt, dass die Funktion beendet wird, auch wenn danach weiterer Code folgt. Ein von einer Funktion zurückgegebener Wert, der ungleich 0 ist, kann vom übergeordneten Prozess als Meldung interpretiert werden, dass das Programm mit einem Fehler beendet wurde.
  3. Überprüft die Gültigkeit des zweiten Befehlszeilenarguments. Wenn es weder „-w“ noch „-a“ ist, dann gibt der bedingte Ausdruck im zweiten if 1 (wahr) zurück. Mit der Funktion strcmp() können Sie Zeichenfolgen vergleichen und 0 zurückgeben, wenn sie gleich sind.
  4. Die for-Schleife öffnet Dateien an den angegebenen Adressen, die mit dem dritten Element des argv-Arrays beginnen. Aus diesem Grund wird 2 zu i hinzugefügt, um die Elemente des argv-Arrays zu erhalten, beginnend mit dem dritten. Der argc-2-Ausdruck gibt die Anzahl der übergebenen Dateinamen an; Weil argc speichert die Gesamtzahl der Befehlszeilenargumente, wobei die ersten beiden keine Dateinamen sind.
  5. Mit dem Ausdruck argv+1 können Sie die Teilzeichenfolge „w“ (oder „a“) ​​aus der Zeichenfolge „-w“ (oder „-a“) „ausschneiden“, weil argv ist im Wesentlichen ein Zeiger auf das erste Element der Zeichenfolge. Indem wir eins zum Zeiger hinzufügen, bewegen wir ihn zum nächsten Element des Arrays.
  6. Wenn die Datei nicht geöffnet werden kann, gibt die Funktion fopen() NULL zurück. In diesem Fall endet das Programm.
  7. Jedes vom Benutzer über die Tastatur eingegebene Zeichen wird in alle geöffneten Dateien geschrieben.
  8. Am Ende werden die Dateien geschlossen.

Eines Tages interessierte ich mich für den Inhalt des Stacks der Hauptfunktion eines Prozesses in Linux. Ich habe ein wenig recherchiert und präsentiere euch nun das Ergebnis.

Möglichkeiten zur Beschreibung der Hauptfunktion:
1. int main()
2. int main(int argc, char **argv)
3. int main(int argc, char **argv, char **env)
4. int main(int argc, char **argv, char **env, ElfW(auxv_t) auxv)
5. int main(int argc, char **argv, char **env, char **apple)

Argc – Anzahl der Parameter
argv – Nullterminal-Array von Zeigern auf Bef
env ist ein Nullterminal-Array von Zeigern auf Zeichenfolgen von Umgebungsvariablen. Jede Zeile im Format NAME=VALUE
auxv – Array von Hilfswerten (nur für PowerPC verfügbar)
apple – Pfad zur ausführbaren Datei (unter MacOS und Darwin)
Ein Hilfsvektor ist ein Array mit verschiedenen Zusatzinformationen, wie z. B. der effektiven Benutzerkennung, dem Setuid-Bit-Attribut, der Speicherseitengröße usw.

Die Größe des Stack-Segments finden Sie in der Maps-Datei:
cat /proc/10918/maps

7ffffffa3000-7ffffffff000 rw-p 00000000 00:00 0

Bevor der Loader die Steuerung an main überträgt, initialisiert er den Inhalt der Arrays mit Befehlszeilenparametern, Umgebungsvariablen und Hilfsvektoren.
Nach der Initialisierung sieht die Oberseite des Stapels für die 64-Bit-Version in etwa so aus.
Seniorenadresse oben.

1. 0x7ffffffff000 Der oberste Punkt des Stapelsegments. Der Aufruf verursacht einen Segfault
0x7ffffffff0f8 NULL Leere* 8 0x00"
2. Dateiname verkohlen 1+ „/tmp/a.out“
verkohlen 1 0x00
...
env verkohlen 1 0x00
...
verkohlen 1 0x00
3. 0x7fffffffe5e0 env verkohlen 1 ..
verkohlen 1 0x00
...
argv verkohlen 1 0x00
...
verkohlen 1 0x00
4. 0x7fffffffe5be argv verkohlen 1+ „/tmp/a.out“
5. Array mit zufälliger Länge
6. Daten für Auxv Leere* 48"
AT_NULL Elf64_auxv_t 16 {0,0}
...
auxv Elf64_auxv_t 16
7. auxv Elf64_auxv_t 16 Bsp.: (0x0e,0x3e8)
NULL Leere* 8 0x00
...
env verkohlen* 8
8. 0x7fffffffe308 env verkohlen* 8 0x7fffffffe5e0
NULL Leere* 8 0x00
...
argv verkohlen* 8
9. 0x7fffffffe2f8 argv verkohlen* 8 0x7fffffffe5be
10. 0x7fffffffe2f0 argc lange int 8" Anzahl der Argumente + 1
11. Lokale Variablen und Argumente von Funktionen, die vor main aufgerufen werden
12. Lokale Variablen main
13. 0x7fffffffe1fc argc int 4 Anzahl der Argumente + 1
0x7fffffffe1f0 argv verkohlen** 8 0x7fffffffe2f8
0x7fffffffe1e8 env verkohlen** 8 0x7fffffffe308
14. Lokale Funktionsvariablen

" - Ich habe in den Dokumenten keine Beschreibungen der Felder gefunden, aber sie sind im Dump deutlich sichtbar.

Ich habe nicht nach 32 Bit gesucht, aber höchstwahrscheinlich reicht es aus, die Größen einfach durch zwei zu teilen.

1. Der Zugriff auf Adressen über dem obersten Punkt führt zu einem Segfault.
2. Eine Zeichenfolge, die den Pfad zur ausführbaren Datei enthält.
3. Array von Strings mit Umgebungsvariablen
4. Array von Strings mit Befehlszeilenparametern
5. Array zufälliger Länge. Seine Auswahl kann mit den Befehlen deaktiviert werden
sysctl -w kernel.randomize_va_space=0
echo 0 > /proc/sys/kernel/randomize_va_space
6. Daten für den Hilfsvektor (zum Beispiel die Zeichenfolge „x86_64“)
7. Hilfsvektor. Weitere Details weiter unten.
8. Nullterminal-Array von Zeigern auf Zeichenfolgen von Umgebungsvariablen
9. Nullterminal-Array von Zeigern auf Bef
10. Ein Maschinenwort, das die Anzahl der Befehlszeilenparameter enthält (eines der Argumente der „Hauptfunktionen“, siehe Abschnitt 11)
11. Lokale Variablen und Argumente von Funktionen, die vor main(_start,__libc_start_main..) aufgerufen werden
12. In main deklarierte Variablen
13.Argumente der Hauptfunktion
14. Variablen und Argumente lokaler Funktionen.

Hilfsvektor
Für i386 und x86_64 ist es nicht möglich, die Adresse des ersten Elements des Hilfsvektors abzurufen, der Inhalt dieses Vektors kann jedoch auf andere Weise abgerufen werden. Eine davon besteht darin, auf den Speicherbereich direkt hinter dem Array von Zeigern auf Zeichenfolgen von Umgebungsvariablen zuzugreifen.
Es sollte ungefähr so ​​aussehen:
#enthalten #enthalten int main(int argc, char** argv, char** env)( Elf64_auxv_t *auxv; //x86_64 // Elf32_auxv_t *auxv; //i386 while(*env++ != NULL); //auf der Suche nach dem Anfang des Hilfsvektor für ( auxv = (Elf64_auxv_t *)env; auxv->a_type != AT_NULL; auxv++)( printf("addr: %p type: %lx is: 0x%lx\n", auxv, auxv->a_type, auxv->a_un .a_val); ) printf("\n (void*)(*argv) - (void*)auxv= %p - %p = %ld\n (void*)(argv)-(void* )(&auxv) =%p-%p = %ld\n ", (void*)(*argv), (void*)auxv, (void*)(*argv) - (void*)auxv, (void* )(argv) , (void*)(&auxv), (void*)(argv) - (void*)(&auxv)); printf("\n argc copy: %d\n",*((int *) (argv - 1 ))); return 0; )
Die Elf(32,64)_auxv_t-Strukturen werden in /usr/include/elf.h beschrieben. Funktionen zum Füllen von Strukturen in linux-kernel/fs/binfmt_elf.c

Zweite Möglichkeit, den Inhalt eines Vektors abzurufen:
hexdump /proc/self/auxv

Die am besten lesbare Darstellung wird durch Festlegen der Umgebungsvariablen LD_SHOW_AUXV erreicht.

LD_SHOW_AUXV=1 ls
AT_HWCAP: bfebfbff //Prozessorfunktionen
AT_PAGESZ: 4096 //Speicherseitengröße
AT_CLKTCK: 100 //Häufigkeitszeiten aktualisieren()
AT_PHDR: 0x400040 //Header-Informationen
AT_PHENT: 56
AT_PHNUM: 9
AT_BASE: 0x7fd00b5bc000 // Interpreteradresse, also ld.so
AT_FLAGS: 0x0
AT_ENTRY: 0x402490 //Programmeinstiegspunkt
AT_UID: 1000 //Benutzer- und Gruppenkennungen
AT_EUID: 1000 //nominal und effektiv
AT_GID: 1000
AT_EGID: 1000
AT_SECURE: 0 //ob das Setuid-Flag gesetzt ist
AT_RANDOM: 0x7fff30bdc809 //Adresse von 16 Zufallsbytes,
beim Start generiert
AT_SYSINFO_EHDR: 0x7fff30bff000 //Zeiger auf die Seite, die für verwendet wird
//Systemaufrufe
AT_EXECFN: /bin/ls
AT_PLATFORM: x86_64
Links steht der Name der Variablen, rechts der Wert. Alle möglichen Variablennamen und deren Beschreibungen finden Sie in der Datei elf.h. (Konstanten mit AT_-Präfix)

Rückkehr von main()
Nachdem der Prozesskontext initialisiert wurde, wird die Steuerung nicht an main(), sondern an die Funktion _start() übergeben.
main() wurde bereits von __libc_start_main aufgerufen. Diese letzte Funktion hat eine interessante Funktion: Ihr wird ein Zeiger auf eine Funktion übergeben, die nach main() ausgeführt werden soll. Und dieser Zeiger wird auf natürliche Weise durch den Stapel weitergeleitet.
Im Allgemeinen sehen die Argumente für __libc_start_main gemäß der Datei glibc-2.11/sysdeps/ia64/elf/start.S so aus
/*
* Argumente für __libc_start_main:
* out0: Haupt
* out1: argc
* out2: argv
* out3:init
* out4: fini //Funktion wird nach main aufgerufen
* out5: rtld_fini
* out6: stack_end
*/
Diese. Um die Adresse des Fini-Zeigers zu erhalten, müssen Sie zwei Maschinenwörter von der letzten lokalen Variablen main verschieben.
Folgendes ist passiert (die Funktionsfähigkeit hängt von der Compilerversion ab):
#enthalten ungültig **ret; ungültig *verlassen; void foo())( void (*boo)(void); //Funktionszeiger printf("Stack rewrite!\n"); boo = (void (*)(void))leave; boo(); // fini () ) int main(int argc, char *argv, char *envp) ( unsigned long int mark = 0xbfbfbfbfbfbfbfbf; //Markierung, von der aus wir arbeiten werden ret = (void**)(&mark+2); // extrahiere die Adresse, eine Funktion, die nach Abschluss aufgerufen wird (fini) Leave = *ret; // merken *ret = (void*)foo; // grind return 0; // Funktion aufrufen foo() )

Ich hoffe, es war interessant.
Viel Glück.

Vielen Dank an Benutzer Xeor für den nützlichen Tipp.

Fortsetzung des Themas:
Router

Bei den meisten E-Mail-Clients, darunter Gmail, Mail.ru, Microsoft Outlook und Mozilla Thunderbird, können Sie mehrere Empfänger in das Cc-Feld (auf Englisch) einfügen.