Raz som sa začal zaujímať o obsah zásobníka funkcie hlavného 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 možností 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 (v MacOS a Darwin)
Pomocný vektor - pole s rôznymi Ďalšie informácie, ako je efektívne ID užívateľa, setuid bit flag, veľkosť stránky pamäte a podobne.

Veľkosť segmentu zásobníka je možné zobraziť v súbore máp:
cat /proc/10918/maps

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

Predtým, ako zavádzač prenesie riadenie do main, inicializuje obsah polí parametrov príkazového riadku, premenné prostredia, pomocný vektor.
Po inicializácii vyzerá horná časť zásobníka asi takto pre 64bitovú 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, funkcie volané pred main
12. lokálne premenné hlavné
13. 0x7fffffffe1fc argc int 4 počet argumentov + 1
0x7fffffffe1f0 argv char** 8 0x7fffffffe2f8
0x7fffffffe1e8 env char** 8 0x7fffffffe308
14. Premenné miestne funkcie

“ – Popisy polí som v dokumentoch nenašiel, ale na smetisku sú dobre viditeľné.

Pre 32 bitov som to nekontroloval, 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 možnosťami 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. Nulové 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 „vyšších“ funkcií, pozri bod 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 nemôžete 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 ihneď po poli 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); //nájsť začiatok pomocnej 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 copy: %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 dosiahne nastavením premenná prostredia LD_SHOW_AUXV.

LD_SHOW_AUXV=1ls
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, t.j. ld.so
AT_FLAGS: 0x0
AT_ENTRY: 0x402490 //vstupný bod programu
AT_UID: 1000 //ID používateľov a skupín
AT_EUID: 1000 //nominálna a účinná
AT_GID: 1000
AT_EGID: 1000
AT_SECURE: 0 //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. (konštanty s predponou AT_)

Návrat z main()
Po inicializácii kontextu procesu sa riadenie prenesie nie do main(), ale do funkcie _start().
main() už volá z __libc_start_main. Toto posledná funkcia má zaujímavú vlastnosť – odovzdá sa mu ukazovateľ na funkciu, ktorá sa má vykonať po funkcii main(). A tento ukazovateľ prirodzene prechádza cez zásobník.
Vo všeobecnosti majú argumenty __libc_start_main tvar 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 presunúť 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; // označenie pre prácu od ret = (void**)(&mark+2); // extrahovanie adresy , funkcia volané po dokončení (fini) opustiť = *ret; // uložiť *ret = (void*)foo; // prepísať návrat 0; // zavolať foo() )

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

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

Pri vytváraní konzolovej aplikácie v programovacom jazyku C++ sa automaticky vytvorí riadok veľmi podobný tomuto:

int main(int argc, char* argv) // parametre funkcie main().

Tento riadok je hlavička hlavná funkcia main() , parametre argс a argv sú uvedené v zátvorkách. Ak je teda program spustený príkazový riadok, potom je možné odovzdať nejaké informácie tomuto programu, na to sú parametre argc a argv . Parameter argc je dátového typu int a obsahuje počet parametrov odovzdaných hlavná funkcia. Navyše argc je vždy aspoň 1, aj keď neodovzdáme žiadne informácie, pretože názov funkcie sa považuje za prvý parameter. Parameter argv je pole ukazovateľov na reťazce. Cez príkazový riadok možno preniesť iba údaje typu reťazec. Ukazovatele a reťazce sú dve veľké témy, pre ktoré boli vytvorené samostatné sekcie. Takže všetky informácie sa prenášajú cez parameter argv. Vyvinieme program, ktorý spustíme cez príkazový riadok. Reťazec systému Windows a odovzdať mu nejaké informácie.

// argc_argv.cpp: Určuje vstupný bod pre aplikáciu konzoly. #include "stdafx.h" #include pomocou menného priestoru std; int main(int argc, char* argv) ( if (argc ><< argv<

// kód Kód::Blocks

// Kód Dev-C++

// argc_argv.cpp: Určuje vstupný bod pre aplikáciu konzoly. #include pomocou menného priestoru std; int main(int argc, char* argv) ( ak (argc > 1)// ak odovzdáme argumenty, potom argc bude väčšie ako 1 (v závislosti od počtu argumentov) ( cout<< argv<

Po odladení programu otvorte príkazový riadok Windows a pretiahnite spustiteľný súbor nášho programu do okna príkazového riadka, na príkazovom riadku sa zobrazí úplná cesta k programu (cestu k programu však môžete napísať ručne), po že môžete stlačiť ENTER a program sa spustí (pozri obrázok 1).

Obrázok 1 - Parametre hlavnej funkcie

Keďže sme program len spustili a neodovzdali sme mu žiadne argumenty, objavilo sa hlásenie Nie sú argumenty. Obrázok 2 ukazuje spustenie toho istého programu cez príkazový riadok, ale s argumentom Otvoriť.

Obrázok 2 - Parametre hlavnej funkcie

Argumentom je slovo Open , ako môžete vidieť na obrázku, toto slovo sa objavilo na obrazovke. Môžete odovzdať niekoľko parametrov naraz, pričom ich oddelíte čiarkou. Ak je potrebné zadať parameter pozostávajúci z niekoľkých slov, musia byť uvedené v úvodzovkách a tieto slová sa budú považovať za jeden parameter. Na obrázku je napríklad znázornené spustenie programu a odovzdanie argumentu pozostávajúceho z dvoch slov - Funguje to.

Obrázok 3 - Parametre hlavnej funkcie

A ak odstránite úvodzovky. Potom uvidíme iba slovo To. Ak neplánujete odovzdávať žiadne informácie pri spustení programu, potom môžete odstrániť argumenty vo funkcii main(), môžete tiež zmeniť názvy týchto argumentov. Niekedy dochádza k úpravám parametrov argc a argv, ale všetko závisí od typu vytváranej aplikácie alebo od vývojového prostredia.

Voliteľné a pomenované argumenty

Voliteľné argumenty

C# 4.0 predstavuje novú funkciu, ktorá zlepšuje pohodlie pri zadávaní argumentov pri volaní metódy. Tento nástroj sa nazýva voliteľné argumenty a umožňuje vám definovať predvolenú hodnotu pre parameter metódy. Táto hodnota sa použije štandardne, ak nie je pre parameter pri volaní metódy zadaný žiadny zodpovedajúci argument. Preto nie je potrebné špecifikovať argument pre takýto parameter. Voliteľné argumenty uľahčujú volanie metód, kde sa na niektoré parametre aplikujú predvolené argumenty. Dajú sa použiť aj ako „krátka“ forma preťaženia metódy.

Hlavným impulzom pre pridávanie voliteľných argumentov bola potreba zjednodušiť interakciu s objektmi COM. V niekoľkých objektových modeloch Microsoft (napríklad Microsoft Office) je funkčnosť poskytovaná prostredníctvom objektov COM, z ktorých mnohé boli napísané už dávno a boli navrhnuté tak, aby používali voliteľné parametre.

Príklad použitia voliteľných argumentov je uvedený nižšie:

Používanie systému; pomocou System.Collections.Generic; pomocou System.Linq; pomocou System.Text; menný priestor ConsoleApplication1 ( trieda Program ( // Argumenty b a c sú voliteľné pri volaní static int mySum(int a, int b = 5, int c = 10) ( return a + b + c; ) static void Main() ( int sum1 = mySum(3); int sum2 = mySum(3,12); Console.WriteLine("Sum1 = "+sum1); Console.WriteLine("Sum2 = "+sum2); Console.ReadLine(); ) ) )

Treba mať na pamäti, že všetky voliteľné argumenty musia byť nevyhnutne uvedené vpravo od požadovaných. Okrem metód možno v konštruktoroch, indexátoroch a delegátoch použiť aj voliteľné argumenty.

Jednou z výhod voliteľných argumentov je, že uľahčujú programátorovi zvládať zložité volania metód a konštruktorov. Koniec koncov, v metóde je často potrebné nastaviť viac parametrov, ako sa zvyčajne vyžaduje. A v prípadoch, ako je tento, môžu byť niektoré z týchto parametrov nastavené ako voliteľné opatrným používaním voliteľných argumentov. To znamená, že je potrebné odovzdať len tie argumenty, ktoré sú v tomto konkrétnom prípade dôležité, a nie všetky argumenty, ktoré by sa inak mali vyžadovať. Tento prístup nám umožňuje racionalizovať metódu a zjednodušiť programátorovi manipuláciu s ňou.

Pomenované argumenty

Ďalšou funkciou, ktorá bola pridaná do C# s vydaním .NET 4.0 je podpora pre tzv pomenované argumenty. Ako viete, pri odovzdávaní argumentov metóde sa poradie, v ktorom sa objavujú, spravidla musí zhodovať s poradím, v ktorom sú parametre definované v samotnej metóde. Inými slovami, hodnota argumentu je priradená parametru podľa jeho pozície v zozname argumentov.

Pomenované argumenty sú navrhnuté tak, aby prekonali toto obmedzenie. Pomenovaný argument vám umožňuje zadať názov parametra, ktorému je priradená jeho hodnota. A v tomto prípade už na poradí argumentov nezáleží. Pomenované argumenty sú teda do istej miery podobné inicializátorom objektov spomenutým vyššie, hoci sa od nich líšia svojou syntaxou. Ak chcete zadať argument podľa názvu, použite nasledujúcu formu syntaxe:

názov_parametra: hodnota

Tu názov_parametra označuje názov parametra, ktorému sa odovzdáva hodnota. Samozrejme, parameter_name musí byť názov platného parametra pre volanú metódu.

Programom C môžete odovzdať určité argumenty. Keď sa na začiatku výpočtu zavolá main(), odovzdajú sa mu tri parametre. Prvý z nich určuje počet argumentov príkazu pri prístupe k programu. Druhým je pole ukazovateľov na reťazce znakov, ktoré obsahujú tieto argumenty (jeden argument na reťazec). Tretím je tiež pole ukazovateľov na znakové reťazce, slúži na prístup k parametrom operačného systému (premenné prostredia).

Každý takýto riadok je reprezentovaný ako:

premenná = hodnota\0

Posledný riadok nájdete podľa dvoch koncových núl.

Pomenujme argumenty funkcie main(): argc, argv a env (možné sú aj iné názvy). Potom sú povolené nasledujúce popisy:

main(int argc, char *argv)

main(int argc, char *argv, char *env)

Predpokladajme, že na jednotke A: je nejaký program prog.exe. Riešime to takto:

A:\>prog.exe súbor1 súbor2 súbor3

Potom argv je ukazovateľ na reťazec A:\prog.exe, argv je ukazovateľ na reťazec file1 atď. Na prvý aktuálny argument poukazuje argv a na posledný argv. Ak argc=1, potom za názvom programu na príkazovom riadku nie sú žiadne parametre. V našom príklade argc=4.

rekurzia

Rekurzia je metóda volania, pri ktorej funkcia volá samu seba.

Dôležitým bodom pri zostavovaní rekurzívneho programu je organizácia výstupu. Tu je ľahké urobiť chybu, že funkcia sa bude dôsledne volať donekonečna. Preto rekurzívny proces musí krok za krokom problém zjednodušiť tak, aby sa preň nakoniec objavilo nerekurzívne riešenie. Použitie rekurzie nie je vždy žiaduce, pretože môže viesť k pretečeniu zásobníka.

Funkcie knižnice

V programovacích systémoch sa podprogramy na riešenie bežných problémov spájajú do knižníc. Tieto úlohy zahŕňajú: výpočet matematických funkcií, vstup/výstup dát, spracovanie reťazcov, interakciu s nástrojmi operačného systému atď. Používanie knižničných podprogramov zbavuje používateľa potreby vývoja vhodných nástrojov a poskytuje mu doplnkovú službu. Funkcie obsiahnuté v knižniciach sú dodávané s programovacím systémom. Ich deklarácie sú uvedené v súboroch *.h (ide o tzv. include alebo hlavičkové súbory). Preto, ako je uvedené vyššie, na začiatku programu s funkciami knižnice by mali byť riadky ako:

#include<включаемый_файл_типа_h>

Napríklad:

#include

K dispozícii sú tiež zariadenia na rozširovanie a vytváranie nových knižníc s používateľskými programami.

Globálnym premenným je počas trvania programu priradené pevné miesto v pamäti. Lokálne premenné sú uložené v zásobníku. Medzi nimi je pamäťová oblasť pre dynamickú alokáciu.

Funkcie malloc() a free() sa používajú na dynamické prideľovanie voľnej pamäte. Funkcia malloc() alokuje pamäť, funkcia free() ju uvoľní. Prototypy týchto funkcií sú uložené v hlavičkovom súbore stdlib.h a vyzerajú takto:

void *malloc(veľkosť_t veľkosť);

void *free(void *p);

Funkcia malloc() vracia ukazovateľ typu void; pre správne použitie musí byť hodnota funkcie prevedená na ukazovateľ na príslušný typ. Po úspechu funkcia vráti ukazovateľ na prvý bajt voľnej pamäte veľkosti. Ak nie je dostatok pamäte, vráti sa hodnota 0. Operácia sizeof() sa používa na určenie počtu bajtov potrebných pre premennú.

Príklad použitia týchto funkcií:

#include

#include

p = (int *) malloc (100 * sizeof (int)); /* Pridelenie pamäte pre 100

celé čísla */

printf("Nedostatok pamäte\n");

pre (i = 0; i< 100; ++i) *(p+i) = i; /* Использование памяти */

pre (i = 0; i< 100; ++i) printf("%d", *(p++));

zadarmo(p); /* Voľná ​​pamäť */

Pred použitím ukazovateľa vráteného pomocou malloc() sa musíte uistiť, že máte dostatok pamäte (ukazovateľ nie je null).

Predprocesor

C preprocesor je program, ktorý spracováva vstup do kompilátora. Preprocesor sa pozrie na zdrojový program a vykoná tieto akcie: pripojí k nemu dané súbory, vykoná substitúcie a spravuje aj podmienky kompilácie. Preprocesor je určený pre programové riadky, ktoré začínajú symbolom #. Na jeden riadok je povolený iba jeden príkaz (direktíva preprocesora).

smernice

#define nahradenie identifikátora

spôsobí, že nasledujúci text programu nahradí pomenovaný identifikátor náhradným textom (všimnite si, že na konci tohto príkazu chýba bodkočiarka). Táto smernica v podstate zavádza definíciu makra (makro), kde „identifikátor“ je názov definície makra a „náhrada“ je postupnosť znakov, ktorými preprocesor nahradí zadaný názov, keď ho nájde v texte programu. Názov makra sa zvyčajne píše veľkými písmenami.

Zvážte príklady:

Prvý riadok spôsobí, že program nahradí identifikátor MAX konštantou 25. Druhý umožňuje použiť v texte namiesto otváracej zloženej zátvorky (() slovo BEGIN.

Upozorňujeme, že keďže preprocesor nekontroluje kompatibilitu medzi symbolickými názvami definícií makier a kontextom, v ktorom sa používajú, odporúča sa definovať takéto identifikátory nie direktívou #define, ale kľúčovým slovom const s explicitným označením typu. (to platí skôr pre C + +):

const int MAX = 25;

(typ int možno vynechať, pretože je štandardne nastavený).

Ak direktíva #define vyzerá takto:

#define identifier(identifikátor, ..., identifikátor) ​​substitúcia

a medzi prvým identifikátorom a úvodnou zátvorkou nie je medzera, potom ide o definíciu makro substitúcie s argumentmi. Napríklad, keď sa objaví riadok ako:

#define READ(val) scanf("%d", &val)

príkaz READ(y); sa spracuje rovnako ako scanf("%d",&y);. Tu je argument val a substitúcia makra sa vykonáva s argumentom.

Ak sú v nahradení dlhé definície, ktoré pokračujú na ďalšom riadku, na koniec nasledujúceho riadku sa umiestni znak \.

Do definície makra môžete umiestniť objekty oddelené znakmi ##, napríklad:

#define PR(x, y) x##y

Potom PR(a, 3) zavolá substitúciu a3. Alebo napríklad definícia makra

#define z(a, b, c, d) a(b##c##d)

zmení z(sin, x, +, y) na sin(x+y).

Znak # umiestnený pred argument makra znamená, že sa skonvertuje na reťazec. Napríklad po smernici

#define PRIM(var) printf(#var"= %d", var)

nasledujúci fragment textu programu

sa prevádza takto:

printf("rok""= %d", rok);

Poďme si popísať ďalšie direktívy preprocesora. Smernicu #include sme už videli. Môže byť použitý v dvoch formách:

#include "názov súboru"

#include<имя файла>

Účinok oboch príkazov je zahrnutie súborov so zadaným názvom do programu. Prvý načíta súbor z aktuálneho adresára alebo adresára zadaného ako prefix. Druhý príkaz vyhľadá súbor na štandardných miestach definovaných v programovacom systéme. Ak sa súbor, ktorého názov je napísaný v úvodzovkách, nenájde v zadanom adresári, vyhľadávanie bude pokračovať v podadresároch zadaných pre príkaz #include<...>. Direktívy #include môžu byť vnorené do seba.

Ďalšia skupina direktív umožňuje selektívne zostavovať časti programu. Tento proces sa nazýva podmienená kompilácia. Táto skupina zahŕňa direktívy #if, #else, #elif, #endif, #ifdef, #ifndef. Základná forma smernice #if je:

#if konštantný_výraz príkazová_sekvencia

Tu sa kontroluje hodnota konštantného výrazu. Ak je pravdivá, vykoná sa daná postupnosť príkazov a ak je nepravdivá, táto postupnosť príkazov sa preskočí.

Činnosť direktívy #else je podobná činnosti príkazu else v jazyku C, napríklad:

#if konštantný_výraz

príkaz_sekvencia_2

Ak je konštantný výraz pravdivý, potom sa vykoná sekvencia_operátorov_1 a ak je nepravdivá, vykoná sa sekvencia_operátorov_2.

Direktíva #elif znamená akciu typu „else if“. Hlavná forma jeho použitia je nasledovná:

#if konštantný_výraz

príkaz_sekvencia

#elif konštantný_výraz_1

príkaz_sekvencia_1

#elif konštantný_výraz_n

postupnosť_výrokov_n

Tento formulár je podobný konštruktu jazyka C formulára: if...else if...else if...

smernice

#ifdef identifikátor

nastavuje, či je zadaný identifikátor aktuálne definovaný, t.j. či to bolo zahrnuté v smerniciach vo forme #define. Zobraziť riadok

#ifndef identifikátor

skontroluje, či zadaný identifikátor momentálne nie je definovaný. Po ktorejkoľvek z týchto direktív môže nasledovať ľubovoľný počet riadkov textu, prípadne obsahujúcich príkaz #else (nie je možné použiť #elif) a končiacich riadkom #endif. Ak je kontrolovaná podmienka pravdivá, potom sa ignorujú všetky riadky medzi #else a #endif, a ak je nepravda, potom riadky medzi kontrolou a #else (ak nie je žiadne slovo #else, potom #endif). Direktívy #if a #ifndef môžu byť vnorené jedna do druhej.

Pozri smernicu

#undef identifikátor

spôsobí, že uvedený identifikátor bude považovaný za nedefinovaný, t.j. nevymeniteľné.

Zvážte príklady. Ide o tieto tri smernice:

skontrolujte, či je definovaný identifikátor ZÁPIS (t.j. bol to príkaz v tvare #define ZÁPIS...), a ak áno, potom sa názov ZÁPIS považuje za nedefinovaný, t.j. nevymeniteľné.

smernice

#define PÍSAŤ fprintf

skontrolujte, či je identifikátor WRITE nedefinovaný, a ak áno, potom sa namiesto názvu fprintf určí identifikátor WRITE.

Smernica #error je napísaná v nasledujúcom tvare:

#error error_message

Ak sa vyskytne v texte programu, kompilácia sa zastaví a na obrazovke sa zobrazí chybové hlásenie. Tento príkaz sa používa hlavne počas fázy ladenia. Upozorňujeme, že chybové hlásenie nemusí byť uzavreté do dvojitých úvodzoviek.

Direktíva #line je určená na zmenu hodnôt premenných _LINE_ a _FILE_ definovaných v programovacom systéme C. Premenná _LINE_ obsahuje číslo riadku práve vykonávaného programu. Identifikátor _FILE_ je ukazovateľ na reťazec s názvom kompilovaného programu. Smernica #line je napísaná takto:

#line number "filename"

Číslo je tu akékoľvek kladné celé číslo, ktoré bude priradené premennej _LINE_, názov súboru je voliteľný parameter, ktorý prepíše hodnotu _FILE_.

Direktíva #pragma vám umožňuje odovzdať niektoré inštrukcie kompilátoru. Napríklad linka

označuje, že v programe C sú reťazce v jazyku symbolických inštancií. Napríklad:

Zvážte niektoré globálne identifikátory alebo názvy makier (názvy definícií makier). Je definovaných päť takýchto názvov: _LINE_, _FILE_, _DATE_, _TIME_, _STDC_. Dva z nich (_LINE_ a _FILE_) už boli opísané vyššie. Identifikátor _DATE_ určuje reťazec, ktorý ukladá dátum, kedy bol zdrojový súbor preložený do objektového kódu. Identifikátor _TIME_ určuje reťazec, v ktorom je uložený čas, kedy bol zdrojový súbor preložený do objektového kódu. Makro _STDC_ má hodnotu 1, ak sa používajú štandardne definované názvy makier. V opačnom prípade táto premenná nebude definovaná.


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

CC 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 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 nejaký argument príkazového riadku. 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ť poskytnutý v programe pri jeho vývoji.

Tu je jednoduchý príklad použitia argumentu príkazového riadka. Na obrazovke sa zobrazí slovo Hello a vaše meno, ktoré je potrebné 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 pomenovali tento program názov (meno) a vaše meno je Tom, potom na spustenie programu zadajte do príkazového riadku 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 navzájom 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

je reťazec jedného znaku – čiarky sa vo všeobecnosti nepovažujú za oddeľovače.

Ak reťazec obsahuje medzery, potom v niektorých prostrediach môže byť reťazec uzavretý do dvojitých úvodzoviek, aby sa zabránilo použitiu 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 sú nastavené možnosti príkazového riadka vo vašom operačnom systéme, pozrite si dokumentáciu k danému systému.

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

Char *argv;

Prázdne hranaté zátvorky označujú, že pole má neurčitú dĺžku. Teraz môžete pristupovať k jednotlivým argumentom 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 nižšie uvedený program odpočítavania. Tento program odpočítava od určitej hodnoty (zadanej na príkazovom riadku) a pípne, keď dosiahne 0. Všimnite si, že prvý argument obsahujúci počiatočnú hodnotu sa skonvertuje na celočíselné hodnoty pomocou štandardnej funkcie atoi () . Ak je druhým argumentom príkazového riadka (a ak vezmeme názov programu ako tretí argument) reťazec „display“ (výstup na obrazovku), zobrazí sa výsledok odpočítavania (v opačnom poradí). na obrazovke.

/* Program na spätné počítanie. */ #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 s argumentmi 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 jednému znaku v jednom 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.

Zvyčajne sa argc a argv používajú na odovzdanie počiatočných príkazov programu, ktoré bude potrebovať pri spustení. Napríklad argumenty príkazového riadka často špecifikujú 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 pomenovať, ako chcete. Niektoré kompilátory môžu tiež podporovať -additional argumenty pre main(), takže si nezabudnite pozrieť dokumentáciu pre váš kompilátor.

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