C získa hlavné argumenty z funkcie. Argumenty hlavnej funkcie. Používanie argumentov príkazového riadka


Niekedy je užitočné pri spustení programu odovzdať mu nejaké informácie. Tieto informácie sa zvyčajne odovzdávajú do main() prostredníctvom argumentov príkazového riadka. Argument príkazového riadka je informácia, ktorá sa zadáva do príkazového riadka operačného systému za názvom programu. Napríklad, ak chcete začať kompilovať program, musíte do príkazového riadka po výzve napísať niečo ako nasledovné:

Kópia názov_programu

názov_programu je argument príkazového riadka, ktorý určuje názov programu, ktorý sa chystáte skompilovať.

Na prijatie argumentov príkazového riadka sa používajú dva špeciálne vstavané argumenty: argc a argv. Parameter argc obsahuje počet argumentov na príkazovom riadku a je to celé číslo a vždy je to aspoň 1, pretože prvý argument je názov programu. A parameter argv je ukazovateľ na pole ukazovateľov na reťazce. V tomto poli každý prvok ukazuje na argument príkazového riadka. Všetky argumenty príkazového riadku sú reťazce, takže prevod akýchkoľvek čísel do požadovaného binárneho formátu musí byť zabezpečený v programe pri jeho vývoji.

Tu je jednoduchý príklad použitia argumentu príkazového riadka. Na obrazovke sa zobrazí slovo Ahoj a vaše meno, ktoré musíte zadať ako argument príkazového riadka.

#include #include int main(int argc, char *argv) ( if(argc!=2) ( printf("Zabudli ste zadať svoje meno.\n"); exit(1); ) printf("Ahoj %s", argv) ; vrátiť 0 ;)

Ak ste tento program nazvali názvom (meno) a vaše meno je Tom, potom na spustenie programu by ste mali do príkazového riadku zadať meno Tom. V dôsledku spustenia programu sa na obrazovke objaví správa Ahoj, Tom.

V mnohých prostrediach musia byť všetky argumenty príkazového riadka oddelené medzerou alebo tabulátorom. Čiarky, bodkočiarky a podobné znaky sa nepovažujú za oddeľovače. Napríklad,

Bež Spot, bež

pozostáva z troch reťazcov znakov, zatiaľ čo

Eric, Rick, Fred

predstavuje jeden reťazec znakov - čiarky sa spravidla nepovažujú za oddeľovače.

Ak reťazec obsahuje medzery, v niektorých prostrediach môžete reťazec uzavrieť do dvojitých úvodzoviek, aby ste zabránili vytváraniu viacerých argumentov. Výsledkom je, že celý reťazec sa bude považovať za jeden argument. Ak sa chcete dozvedieť viac o tom, ako váš operačný systém nastavuje parametre príkazového riadka, pozrite si dokumentáciu vášho operačného systému.

Je veľmi dôležité správne deklarovať argv. Najčastejšie sa to robí takto:

Char *argv;

Prázdne hranaté zátvorky označujú, že pole má neurčitú dĺžku. Jednotlivé argumenty sú teraz prístupné indexovaním poľa argv. Napríklad argv ukazuje na prvý znakový reťazec, ktorý je vždy názvom programu; argv poukazuje na prvý argument a tak ďalej.

Ďalším malým príkladom použitia argumentov príkazového riadku je nasledujúci program, odpočítavanie. Tento program počíta spätne, počnúc od nejakej hodnoty (špecifikovanej na príkazovom riadku) a pípne, keď dosiahne 0. Všimnite si, že prvý argument, ktorý obsahuje počiatočnú hodnotu, je konvertovaný na celočíselné hodnoty pomocou štandardnej funkcie atoi (). Ak je druhý argument príkazového riadka (a ak za argument považujeme názov programu, potom tretí) je riadok „zobrazenie“ (zobrazenie), na obrazovke sa zobrazí výsledok počítania (v opačnom poradí).

/* Program na počítanie v opačnom poradí. */ #include #include #include #include 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; }

Upozorňujeme, že ak nie sú zadané argumenty príkazového riadka, zobrazí sa chybové hlásenie. Programy, ktoré používajú argumenty príkazového riadka, často robia nasledovné: Keď používateľ spustí tieto programy bez zadania požadovaných informácií, zobrazia sa pokyny, ako správne zadať argumenty.

Ak chcete získať prístup k jednotlivému znaku jedného z argumentov príkazového riadka, zadajte druhý index do argv. Napríklad nasledujúci program vypíše znak po znaku všetky argumenty, s ktorými bol volaný:

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

Pamätajte, že prvý index argv poskytuje prístup k reťazcu a druhý index poskytuje prístup k jeho jednotlivým znakom.

Typicky sa argc a argv používajú na zadanie počiatočných príkazov programu, ktoré bude potrebovať pri spustení. Napríklad argumenty príkazového riadka často určujú informácie, ako je názov súboru, možnosť alebo alternatívne správanie. Použitie argumentov príkazového riadku dáva vášmu programu "profesionálny vzhľad" a uľahčuje jeho používanie v dávkových súboroch.

Názvy argc a argv sú tradičné, ale nevyžadujú sa. Tieto dva parametre vo funkcii main() môžete volať, ako chcete. Navyše, niektoré kompilátory môžu podporovať ďalšie argumenty pre main(), takže si nezabudnite skontrolovať dokumentáciu kompilátora.

Keď program nevyžaduje parametre príkazového riadka, je najbežnejšie explicitne deklarovať funkciu main() ako bez parametrov. V tomto prípade sa kľúčové slovo void použije v zozname parametrov tejto funkcie.

Značky: Možnosti príkazového riadku

Možnosti príkazového riadku

C je kompilovaný jazyk. Po zostavení je program spustiteľný súbor (neuvažujeme o vytváraní dynamických knižníc, ovládačov a pod.). Naše programy sú veľmi jednoduché a neobsahujú Runtime knižnice, takže ich možno preniesť do počítača s rovnakým operačným systémom (a podobnou architektúrou) a spustiť tam.

Program môže akceptovať parametre počas spúšťania. Sú to argumenty hlavnej funkcie. Celkový pohľad na hlavnú funkciu je nasledujúci

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

Prvý argument argc je počet parametrov odovzdaných funkcii. Druhým argumentom je pole reťazcov – samotné parametre. Keďže parametre funkcie môžu byť akékoľvek, odovzdávajú sa ako reťazce a samotný program ich musí analyzovať a previesť na požadovaný typ.

Prvý argument (argv) je vždy názov programu. V tomto prípade sa názov zobrazí v závislosti od toho, odkiaľ bol program spustený.

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

Teraz sa naučíme, ako trochu pracovať s príkazovým riadkom. To bude potrebné na odovzdanie argumentov nášmu programu. Kombinácia klávesov Win + R vyvolá okno Spustiť. Zadajte tam cmd a otvoríte príkazový riadok. Cmd.exe môžete nájsť aj vyhľadaním v ponuke Štart. V operačných systémoch podobných Unixu môžete zavolať terminálový program.

Nebudeme sa učiť príliš veľa príkazov. Iba tie, ktoré sú potrebné na prácu.

Príkaz cd, štandardný pre všetky operačné systémy, prejde do požadovaného priečinka. Vyhradené sú dve mená - . (bodka) a.. (dve bodky). Bodka je názov aktuálneho priečinka.

Nikam nejde

Prístup k nadradenému priečinku

Prejdite do nadradeného priečinka

Ak chcete prejsť na požadovanú adresu, napíšte adresu CD. Napríklad musíte prejsť do Windows do priečinka C:\Windows\System32

CD C:\Windows\System32

V Linuxe, ak potrebujete prejsť do priečinka /var/mysql

Cd /var/mysql

Ak cesta obsahuje medzery, zapíše sa do dvojitých úvodzoviek

CD "D:\Docuents and Settings\Prolog"

Terminál má nasledujúce užitočné funkcie: ak stlačíte šípku nahor, zobrazí sa predchádzajúci vykonaný príkaz. Ak stlačíte tab, terminál sa pokúsi dokončiť riadok k príkazu, ktorý je mu známy, alebo dokončiť cestu, pričom prejde všetky priečinky a súbory v aktuálnom priečinku.
Napíšte cd C:\
stlačte tab a uvidíte, čo sa stane.

Ďalší dôležitý príkaz, dir v systéme Windows a ls v systéme Linux, zobrazuje obsah aktuálneho priečinka (priečinok, v ktorom sa práve nachádzate) do konzoly.

Váš program vrátil celé meno. Prejdite do priečinka, v ktorom sa nachádza váš program, a pozrite sa na jeho obsah


Teraz, keď sme prešli do nášho priečinka, môžeme spustiť náš program. Ak to chcete urobiť, zadajte jej meno.


Všimnite si, že názov sa zmenil. Keďže sa program volá z vlastného priečinka, zobrazí sa relatívny názov. Teraz zmeňme program a nech vypíše všetky argumenty. ktoré jej boli dané.

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

Zostavte projekt. Pred zostavením sa uistite, že je program zatvorený. Teraz zavolajte program a odovzdajte mu rôzne argumenty. Za týmto účelom napíšte názov programu a argumenty oddelené medzerou


Poďme teraz napísať program, ktorý vezme dva číselné argumenty a vypíše ich súčet

#include #include #include void main(int argc, char **argv) ( int a, b; if (argc != 3) ( printf("Chyba: nájdených %d argumentov. Potrebuje presne 2", argc-1); exit(1); ) a = atoi(argv); b = atoi(argv); printf("%d", a + b); )

Zbierajme a volajme


Takto funguje väčšina programov. Kliknutím na skratku zavoláte program, na ktorý odkazuje. Väčšina programov akceptuje aj rôzne argumenty. Môžete napríklad zavolať prehliadač firefox z príkazového riadku a odovzdať argumenty
firefox.exe "www.mozilla.org" "stránka" a okamžite otvorí stránky na zadaných adresách na dvoch kartách.

Mnoho štandardných príkazov má aj parametre. Vo Windows je zvykom, že začínajú lomkou, v Unixe mínusom alebo dvomi mínusmi. Napríklad

Zobrazuje iba priečinky, ale v linuxovom termináli

Ls -l zobrazí zoznam všetkých súborov a priečinkov s atribútmi

Ak chcete zobraziť ďalšie príkazy systému Windows, napíšte pomoc do príkazového riadka alebo si pozrite príručku (dá sa ľahko nájsť na internete). Pre Linux existuje oveľa viac príkazov a ich možností a niektoré z nich sú nezávislé programovacie jazyky, takže stojí za to naučiť sa aspoň minimálnu sadu a ich možnosti.

Borland C++ podporuje tri argumenty pre main(). Prvé dva sú tradičné argc a argv. Toto sú jediné argumenty pre main() definované štandardom ANSI C. Umožňujú odovzdanie argumentov príkazového riadka programu. Argumenty príkazového riadka sú informácie, ktoré nasledujú za názvom programu na príkazovom riadku operačného systému. Napríklad, keď je program kompilovaný pomocou riadkového kompilátora Borland, zvyčajne sa zadáva bcc názov_programu

Kde názov_programu je program, ktorý je potrebné skompilovať. Názov programu sa odovzdá kompilátoru ako argument.

Parameter argc obsahuje počet argumentov príkazového riadku a je to celé číslo. Vždy sa rovná aspoň 1, pretože názov programu sa kvalifikuje ako prvý argument. Parameter argv je ukazovateľ na pole ukazovateľov znakov. Každý prvok tohto poľa ukazuje na argument príkazového riadka. Všetky argumenty príkazového riadku sú reťazce. Všetky čísla prevedie program do interného formátu. Nasledujúci program vypíše „Ahoj“, za ktorým nasleduje používateľské meno, keď je napísané priamo za názvom programu:

#include

{
if(argc!=2)
{
printf("Zabudli ste zadať svoje meno\n");
návrat 1;
}
printf("Ahoj %s", argv);
návrat 0;
}

Ak zavoláte tento názov programu a používateľské meno je Sergey, na spustenie programu by ste mali zadať:
meno Sergey.
V dôsledku programu sa zobrazí nasledovné:
"Ahoj Sergey."

Argumenty príkazového riadka musia byť oddelené medzerami alebo tabulátormi. Čiarky, bodkočiarky a podobné znaky sa nepovažujú za oddeľovače. Napríklad:

Pozostáva z troch riadkov, pričom

Herb, Rick, Fred

Toto je jeden riadok – čiarky nie sú oddeľovače.

Ak potrebujete zadať reťazec obsahujúci medzery alebo tabulátory ako jeden argument, musíte ho uzavrieť do dvojitých úvodzoviek. Napríklad toto je jeden argument:

"toto je test"

Je dôležité správne deklarovať argv. Najtypickejšia metóda je:

Prázdne zátvorky označujú, že pole nemá pevnú dĺžku. K jednotlivým prvkom sa dostanete pomocou indexovania argv. Napríklad argv ukazuje na prvý riadok, ktorý vždy obsahuje názov programu. argv ukazuje na ďalší riadok a tak ďalej.

Nižšie je uvedený malý príklad použitia argumentov príkazového riadku. Odpočítava od hodnoty zadanej na príkazovom riadku a vyšle signál, keď dosiahne nulu. Všimnite si, že prvý argument obsahuje číslo prevedené na celé číslo pomocou štandardnej funkcie atoi(). Ak je reťazec "zobrazenie" prítomný ako druhý argument, potom sa na obrazovke zobrazí samotné počítadlo.

/* program na počítanie */

#include
#include
#include
int main(int argc, char *argv)
{
int disp, počet;
if(argc<2)
{
printf("Musíte zadať dĺžku počítania\n");
printf("na príkazovom riadku. Skúste to znova.\n");
návrat 1;
}
if (argc==3 && !strcmp(argv,"zobrazit")) disp = 1;
inak disp = 0;
for(count=atoi(argv); count; -count)
if (disp) printf("%d ", pocet);
printf("%c", "\a"); /* na väčšine počítačov ide o volanie */
návrat 0;
}

Upozorňujeme, že ak nie sú zadané žiadne argumenty, zobrazí sa chybové hlásenie. Toto je najtypickejšie pre programy, ktoré používajú argumenty príkazového riadka na zadávanie pokynov, ak bol vykonaný pokus o spustenie programu bez správnych informácií.

Ak chcete získať prístup k jednotlivým znakom príkazového riadka, pridajte do argv druhý index. Napríklad nasledujúci program vypíše všetky argumenty, pomocou ktorých bol volaný, po jednom znaku:

#include
int main(int argc, char *argv)
{
int t, i;
for(t=0; t {
i = 0;
while(argv[t][i])
{
printf("%c", argv[t][i]);
}
printf(" ");
}
návrat 0;
}

Musíme si uvedomiť, že prvý index slúži na prístup k reťazcu a druhý je na prístup k znaku v reťazci.

Typicky sa argc a argv používajú na získanie zdrojových príkazov. Teoreticky je možné mať až 32 767 argumentov, no väčšina operačných systémov vám ani nedovolí sa k tomu priblížiť. Tieto argumenty sa zvyčajne používajú na určenie názvu súboru alebo možností. Použitie argumentov príkazového riadku dáva programu profesionálny vzhľad a umožňuje použitie programu v dávkových súboroch.

Ak zahrniete súbor WILDARGS.OBJ dodávaný s Borland C++, môžete použiť šablóny v argumentoch typu *.EXE. (Borland C++ automaticky spracováva zástupné znaky a zodpovedajúcim spôsobom zvyšuje argc.) Napríklad, ak pripojíte WILDARGS.OBJ k nasledujúcemu programu, povie vám, koľko súborov zodpovedá názvu súboru zadanému v príkazovom riadku:

/* Prepojte tento program s WILDARGS.OBJ */

#include
int main(int argc, char *argv)
{
register int i;
printf("%d súborov zodpovedá zadanému názvu\n", argc-1);
printf("Sú to: ");
pre (i=1; i printf("%s", argv[i]);
návrat 0;
}

Ak tento program nazveme WA, potom ho spustíme nasledovne, dostaneme počet súborov s príponou EXE a zoznam názvov týchto súborov:

Okrem argc a argv poskytuje Borland C++ aj tretí argument príkazového riadka -env. Parameter env umožňuje programu prístup k informáciám o prostredí operačného systému. Parameter env musí nasledovať po argc a argv a je deklarovaný takto:

Ako vidíte, env sa deklaruje rovnakým spôsobom ako argv. Rovnako ako argv, je to ukazovateľ na pole reťazcov. Každý riadok je reťazec prostredia definovaný operačným systémom. Parameter env nemá žiadny ekvivalentný parameter argc, ktorý hovorí, koľko riadkov prostredia existuje. Namiesto toho je posledný riadok prostredia nulový. Nasledujúci program vytlačí všetky reťazce prostredia aktuálne definované v operačnom systéme:

/* tento program zobrazí všetky riadky prostredia */

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

Upozorňujeme, že hoci argc a argv program nepoužíva, musia sa nachádzať v zozname parametrov. C nepozná názvy parametrov. Namiesto toho je ich použitie určené poradím, v ktorom sú parametre deklarované. V skutočnosti môžete parameter nazvať akokoľvek chcete. Keďže argc, argv a env sú tradičné názvy, je najlepšie ich naďalej používať, aby každý, kto číta program, okamžite pochopil, že ide o argumenty funkcie main().

Typickou úlohou programov je vyhľadať hodnotu definovanú v reťazci prostredia. Napríklad obsah riadku PATH umožňuje programom používať vyhľadávacie cesty. Nasledujúci program ukazuje, ako nájsť reťazce, ktoré deklarujú štandardné vyhľadávacie cesty. Používa štandardnú knižničnú funkciu strstr(), ktorá má nasledujúci prototyp:

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

Funkcia strstr() hľadá reťazec, na ktorý ukazuje str1, v reťazci, na ktorý ukazuje str2. Ak sa takýto reťazec nájde, vráti sa ukazovateľ na prvú pozíciu. Ak sa nenájdu žiadne zhody, funkcia vráti hodnotu NULL.

/* program hľadá medzi reťazcami prostredia riadok obsahujúci PATH */

#include
#include
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]);
}
návrat 0;
}

Stáva sa, že údaje sa prenesú do programu z príkazového riadku pri jeho volaní. Takéto údaje sa nazývajú argumenty príkazového riadku. Vyzerá to napríklad takto:

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

Tu sa volajú programy a.out (z aktuálneho adresára) a ls (z rovnakého adresára špecifikovaného v premennej prostredia PATH). Prvý program z príkazového riadku dostane jedno slovo - test.txt, druhý - dve: -lt a /home/peter/.

Ak je program napísaný v jazyku C, po jeho spustení sa riadenie okamžite prenesie do funkcie main(), teda je to funkcia, ktorá prijíma argumenty príkazového riadku, ktoré sú priradené k premenným parametrov.

Predtým sme definovali funkciu main(), ako keby nebrala žiadne parametre a nič nevracala. V skutočnosti v jazyku C akákoľvek funkcia štandardne (ak nie je definované nič iné) vracia celé číslo. Môžete si byť tým istý. Ak kód napíšete takto:

main() ( printf ("Ahoj \n"); návrat 0; )

Potom sa počas kompilácie nevyskytne žiadne varovanie ani chyba. To isté sa stane, ak napíšete int main() . To dokazuje, že funkcia štandardne vracia celé číslo a nie nič (void). Hoci to, čo funkcia vracia, môže byť vždy „prepísané“, napríklad voidmain() alebo float main() .

Pri volaní programu z príkazového riadku sa do neho vždy odovzdá nasledujúca dvojica údajov:

  1. celé číslo s uvedením počtu slov (prvkov oddelených medzerami) na príkazovom riadku pri volaní,
  2. ukazovateľ na pole reťazcov, kde každý riadok je samostatné slovo z príkazového riadku.

Majte na pamäti, že sa počíta aj samotný názov programu. Ak hovor vyzerá napríklad takto:

./a.out 12 téma 2

Potom má prvý argument programu hodnotu 4 a pole reťazcov je definované ako (./a.out“, „12“, „téma“, „2“).

Všimnite si terminológiu, existujú len dva argumenty programu (číslo a pole), ale toľko argumentov príkazového riadku, koľko chcete. Argumenty príkazového riadku sú "konvertované" na argumenty programu (na argumenty funkcie main().
Tieto údaje (číslo a ukazovateľ) sú odovzdané programu, aj keď je jednoducho volaný menom bez toho, aby sa mu čokoľvek odovzdalo: ./a.out. V tomto prípade má prvý argument hodnotu 1 a druhý ukazuje na pole pozostávajúce len z jedného riadku (./a.out).

To, že sa do programu prenášajú údaje, neznamená, že ich funkcia main() musí akceptovať. Ak je funkcia main() definovaná bez parametrov, potom nie je možné získať prístup k argumentom príkazového riadka. Hoci vám nič nebráni v ich prenose. Nedôjde k žiadnej chybe.

Ak chcete získať prístup k údajom odovzdaným programu, musia byť priradené k premenným. Keďže argumenty sú okamžite odovzdané do main(), jej hlavička by mala vyzerať takto:
hlavné (int n, znak *arr)

Prvá premenná (n) obsahuje počet slov a druhá premenná obsahuje ukazovateľ na pole reťazcov. Druhý parameter sa často zapisuje ako **arr . Ide však o to isté. Pripomeňme si, že samotné pole reťazcov obsahuje ako svoje prvky ukazovatele na reťazce. A funkcii odovzdáme ukazovateľ na prvý prvok poľa. Ukazuje sa, že odovzdávame ukazovateľ k ukazovateľu, t.j. **arr.

Cvičenie
Napíšte takýto program:

#include int main(int argc, char ** argv) ( int i; printf ("%d \n", argc); pre (i= 0; i< argc; i++ ) puts (argv[ i] ) ; }

Pri volaní zobrazuje počet slov na príkazovom riadku a každé slovo na novom riadku. Zavolajte to bez argumentov príkazového riadku a s argumentmi.

V programe sme použili premenné parametrov argc a argv. Je zvykom používať tieto mená, ale v skutočnosti to môže byť čokoľvek. Je lepšie sa držať tohto štandardu, aby boli vaše programy zrozumiteľnejšie nielen vám, ale aj ostatným programátorom.

Praktický význam prenosu údajov do programu

Ak máte nejaké skúsenosti s príkazovým riadkom GNU/Linux, viete, že väčšina príkazov má prepínače a argumenty. Napríklad pri prezeraní obsahu adresárov, kopírovaní, presúvaní sú ako argumenty špecifikované objekty súborového systému, na ktorých je príkaz vykonaný. Vlastnosti jeho implementácie sa určujú pomocou kľúčov. Napríklad v tíme

Cp -r ../les_1 ../les_101

cp je názov príkazu, -r je prepínač a ../les_1 a ../les_101 sú argumenty príkazu.

Vo všeobecnosti sa adresy súborov a „modifikátory“ (to sú kľúče) procesu vykonávania programu najčastejšie prenášajú do programov pri ich spustení.

Napíšme program, ktorý otvorí súbory určené používateľom na príkazovom riadku na zápis alebo pridanie a zapíše (pridá) tam rovnaké informácie, ktoré používateľ zadá z klávesnice počas vykonávania programu:

#include #include main (int argc, char ** argv) ( int i, ch; FILE * f[ 5 ] ; if (argc< 3 || argc >7) (ukladá ( "Neplatný počet parametrov"); návrat 1; ) if (strcmp (argv[ 1 ] , "-w" ) != 0 && strcmp (argv[ 1 ], "-a" ) != 0 ) ( vloží ( "Prvý parameter môže byť -w alebo -a"); návrat 2; ) pre (i= 0; i< argc- 2 ; i++ ) { f[ i] = fopen (argv[ i+ 2 ] , argv[ 1 ] + 1 ) ; if (f[ i] == NULL) { printf ("Súbor %s sa nedá otvoriť\n " argv[i+2]); návrat 3; ) ) while ((ch = getchar () ) != EOF) pre (i= 0; i< argc- 2 ; i++ ) putc (ch, f[ i] ) ; for (i= 0 ; i < argc- 2 ; i++ ) fclose (f[ i] ) ; return 0 ; }

Vysvetlenia pre kód:

  1. Vytvorí sa pole piatich ukazovateľov súborov. Preto nemôžete súčasne otvoriť viac ako päť súborov. Ukazovateľ súboru prvého súboru bude uložený v prvku poľa f, druhý - v f atď.
  2. Kontroluje sa počet argumentov príkazového riadku. Mali by byť aspoň tri, pretože... prvý je názov programu, druhý je režim otvárania súboru, tretí je prvý alebo jediný súbor, do ktorého sa má zapisovať. Keďže program umožňuje otvoriť iba päť súborov, celkový počet argumentov príkazového riadku nemôže byť väčší ako sedem. Ak je teda počet argumentov menší ako 3 alebo väčší ako 7, program končí, pretože Príkaz return spôsobí ukončenie funkcie, aj keď je za ním ďalší kód. Hodnotu vrátenú z funkcie, ktorá sa nerovná 0, môže nadradený proces interpretovať ako správu, že program skončil s chybou.
  3. Skontroluje platnosť druhého argumentu príkazového riadka. Ak to nie je ani "-w" ani "-a", potom podmienený výraz v druhom if vráti 1 (pravda). Funkcia strcmp() vám umožňuje porovnávať reťazce a vracia 0, ak sú rovnaké.
  4. Cyklus for otvára súbory na zadaných adresách, ktoré začínajú tretím prvkom poľa argv. To je dôvod, prečo sa k i pridá 2, aby sa získali prvky poľa argv, počnúc od tretieho. Výraz argc-2 označuje počet odovzdaných názvov súborov; pretože argc ukladá celkový počet argumentov príkazového riadku, z ktorých prvé dva nie sú názvy súborov.
  5. Výraz argv+1 vám umožňuje „vystrihnúť“ podreťazec „w“ (alebo „a“) ​​z reťazca „-w“ (alebo „-a“), pretože argv je v podstate ukazovateľ na prvý prvok reťazca. Pridaním jedného do ukazovateľa ho presunieme na ďalší prvok poľa.
  6. Ak sa súbor nedá otvoriť, funkcia fopen() vráti hodnotu NULL. V tomto prípade program končí.
  7. Každý znak zadaný používateľom na klávesnici sa zapíše do všetkých otvorených súborov.
  8. Na konci sú súbory uzavreté.

Jedného dňa som sa začal zaujímať o obsah zásobníka hlavnej funkcie procesu v Linuxe. Urobil som prieskum a teraz vám predstavujem výsledok.

Možnosti popisu hlavnej funkcie:
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 **jablko)

Argc - počet parametrov
argv - null-terminálne pole ukazovateľov na reťazce parametrov príkazového riadku
env je null-terminálne pole ukazovateľov na reťazce premenných prostredia. Každý riadok vo formáte NAME=VALUE
auxv - pole pomocných hodnôt (dostupné iba pre PowerPC)
apple - cesta k spustiteľnému súboru (na MacOS a Darwin)
Pomocný vektor je pole s rôznymi dodatočnými informáciami, ako je efektívny identifikátor používateľa, atribút setuid bit, veľkosť stránky pamäte atď.

Veľkosť segmentu zásobníka nájdete v súbore máp:
cat /proc/10918/maps

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

Predtým, ako zavádzač prenesie riadenie na main, inicializuje obsah polí parametrov príkazového riadka, premenných prostredia a pomocného vektora.
Po inicializácii vyzerá horná časť zásobníka asi takto pre 64-bitovú verziu.
Seniorská adresa hore.

1. 0x7ffffffff000 Horný bod segmentu zásobníka. Hovor spôsobí chybu segfault
0x7ffffffff0f8 NULOVÝ prázdno* 8 0x00"
2. názov súboru char 1+ "/tmp/a.out"
char 1 0x00
...
env char 1 0x00
...
char 1 0x00
3. 0x7fffffffe5e0 env char 1 ..
char 1 0x00
...
argv char 1 0x00
...
char 1 0x00
4. 0x7fffffffe5be argv char 1+ "/tmp/a.out"
5. Pole náhodnej dĺžky
6. údaje pre auxv prázdno* 48"
AT_NULL Elf64_auxv_t 16 {0,0}
...
auxv Elf64_auxv_t 16
7. auxv Elf64_auxv_t 16 Príklad: (0x0e,0x3e8)
NULOVÝ prázdno* 8 0x00
...
env char* 8
8. 0x7fffffffe308 env char* 8 0x7fffffffe5e0
NULOVÝ prázdno* 8 0x00
...
argv char* 8
9. 0x7fffffffe2f8 argv char* 8 0x7fffffffe5be
10. 0x7fffffffe2f0 argc dlhá int 8" počet argumentov + 1
11. Lokálne premenné a argumenty funkcií volaných pred main
12. Hlavné lokálne premenné
13. 0x7fffffffe1fc argc int 4 počet argumentov + 1
0x7fffffffe1f0 argv char** 8 0x7fffffffe2f8
0x7fffffffe1e8 env char** 8 0x7fffffffe308
14. Lokálne funkčné premenné

“ - V dokumentoch som nenašiel popisy polí, ale sú jasne viditeľné na skládke.

Nekontroloval som 32 bitov, ale s najväčšou pravdepodobnosťou stačí rozdeliť veľkosti dvoma.

1. Prístup k adresám nad horným bodom spôsobí Segfault.
2. Reťazec obsahujúci cestu k spustiteľnému súboru.
3. Pole reťazcov s premennými prostredia
4. Pole reťazcov s parametrami príkazového riadku
5. Pole náhodnej dĺžky. Jeho výber je možné vypnúť pomocou príkazov
sysctl -w kernel.randomize_va_space=0
echo 0 > /proc/sys/kernel/randomize_va_space
6. Údaje pre pomocný vektor (napríklad reťazec „x86_64“)
7. Pomocný vektor. Viac podrobností nižšie.
8. Null-terminálne pole ukazovateľov na reťazce premenných prostredia
9. Nulové pole ukazovateľov na reťazce parametrov príkazového riadku
10. Strojové slovo obsahujúce počet parametrov príkazového riadku (jeden z argumentov „hlavných“ funkcií, pozri odsek 11)
11. Lokálne premenné a argumenty funkcií volaných pred main(_start,__libc_start_main..)
12. Premenné deklarované v main
13.Argumenty hlavnej funkcie
14. Premenné a argumenty lokálnych funkcií.

Pomocný vektor
Pre i386 a x86_64 nie je možné získať adresu prvého prvku pomocného vektora, ale obsah tohto vektora je možné získať inými spôsobmi. Jedným z nich je prístup do oblasti pamäte bezprostredne za poľom ukazovateľov na reťazce premenných prostredia.
Malo by to vyzerať asi takto:
#include #include int main(int argc, char** argv, char** env)( Elf64_auxv_t *auxv; //x86_64 // Elf32_auxv_t *auxv; //i386 while(*env++ != NULL); //hľadanie začiatku pomocný vektor pre ( auxv = (Elf64_auxv_t *)env; auxv->a_type != AT_NULL; auxv++)( printf("addr: %p typ: %lx je: 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 kópia: %d\n",*((int *) (argv - 1 ))); návrat 0; )
Štruktúry Elf(32,64)_auxv_t sú opísané v /usr/include/elf.h. Funkcie na vyplnenie štruktúr v linux-kernel/fs/binfmt_elf.c

Druhý spôsob, ako získať obsah vektora:
hexdump /proc/self/auxv

Najčitateľnejšie zobrazenie sa získa nastavením premennej prostredia LD_SHOW_AUXV.

LD_SHOW_AUXV=1 ls
AT_HWCAP: bfebfbff //možnosti procesora
AT_PAGESZ: 4096 //veľkosť stránky pamäte
AT_CLKTCK: 100 //krát frekvencia aktualizácie ()
AT_PHDR: 0x400040 //informácie v hlavičke
AT_PHENT: 56
AT_PHNUM: 9
AT_BASE: 0x7fd00b5bc000 //adresa tlmočníka, to znamená ld.so
AT_FLAGS: 0x0
AT_ENTRY: 0x402490 //vstupný bod programu
AT_UID: 1000 //identifikátory používateľov a skupín
AT_EUID: 1000 //nominálna a účinná
AT_GID: 1000
AT_EGID: 1000
AT_SECURE: 0 //či je nastavený príznak setuid
AT_RANDOM: 0x7fff30bdc809 //adresa 16 náhodných bajtov,
generované pri spustení
AT_SYSINFO_EHDR: 0x7fff30bff000 //ukazovateľ na stránku použitú pre
//systémové volania
AT_EXECFN: /bin/ls
AT_PLATFORM: x86_64
Vľavo je názov premennej, vpravo hodnota. Všetky možné názvy premenných a ich popis nájdete v súbore elf.h. (stále s predponou AT_)

Návrat z main()
Po inicializácii kontextu procesu sa riadenie prenesie nie do main(), ale do funkcie _start().
main() sa už volá z __libc_start_main. Táto posledná funkcia má zaujímavú vlastnosť – odovzdáva sa jej ukazovateľ na funkciu, ktorá by sa mala vykonať po funkcii main(). A tento ukazovateľ prirodzene prechádza cez zásobník.
Vo všeobecnosti argumenty __libc_start_main vyzerajú takto, podľa súboru glibc-2.11/sysdeps/ia64/elf/start.S
/*
* Argumenty pre __libc_start_main:
* out0: hlavný
* out1: argc
* out2: argv
* out3: init
* out4: fini //funkcia volaná po hlavnom
* out5: rtld_fini
* out6: stack_end
*/
Tie. aby ste získali adresu koncového ukazovateľa, musíte posunúť dve strojové slová z poslednej lokálnej premennej main.
Tu je to, čo sa stalo (funkčnosť závisí od verzie kompilátora):
#include void **ret; neplatné *odísť; void foo())( void (*boo)(void); //ukazovateľ funkcie printf("Prepísať zásobník!\n"); boo = (void (*)(void))leave; boo(); // fini () ) int main(int argc, char *argv, char *envp) ( unsigned long int mark = 0xbfbfbfbfbfbfbfbf; //značka, z ktorej budeme pracovať ret = (void**)(&mark+2); // extrahovať address , funkcia volaná po dokončení (fini) Leave = *ret; // zapamätať si *ret = (void*)foo; // grind return 0; // zavolať funkciu foo() )

Dúfam, že to bolo zaujímavé.
Veľa štastia.

Ďakujeme používateľovi Xeor za užitočný tip.

Pokračovanie v téme:
Smerovače

Väčšina e-mailových klientov, vrátane Gmail, Mail.ru, Microsoft Outlook, Mozilla Thunderbird, vám umožňuje vložiť viacerých príjemcov do Cc (v angličtine...