Веднъж се заинтересувах от съдържанието на стека на основната функция на процеса в linux. Направих малко проучване и сега ви представям резултата.

Опции за описание на основната функция:
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 - брой параметри
argv - нулев терминален масив от указатели към низове от опции на командния ред
env е нулев терминален масив от указатели към низове на променливи на средата. Всеки ред във формат NAME=VALUE
auxv - масив от спомагателни стойности (налични само за PowerPC)
apple - път до изпълнимия файл (в MacOS и Darwin)
Спомагателен вектор - масив с различни Допълнителна информация, като ефективен потребителски идентификатор, битов флаг на setuid, размер на страницата на паметта и други подобни.

Размерът на сегмента на стека може да се види във файла с карти:
котка /proc/10918/карти

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

Преди зареждащото устройство да прехвърли управлението на main, то инициализира съдържанието на масивите от параметри на командния ред, променливи на средата, спомагателен вектор.
След инициализация горната част на стека изглежда нещо подобно за 64-битовата версия.
Старши адрес отгоре.

1. 0x7ffffffff000 Горната точка на сегмента на стека. Обаждането причинява segfault
0x7ffffffff0f8 НУЛА празнота* 8 0x00"
2. име на файл въглен 1+ "/tmp/a.out"
въглен 1 0x00
...
околна среда въглен 1 0x00
...
въглен 1 0x00
3. 0x7fffffffe5e0 околна среда въглен 1 ..
въглен 1 0x00
...
argv въглен 1 0x00
...
въглен 1 0x00
4. 0x7fffffffe5be argv въглен 1+ "/tmp/a.out"
5. Масив с произволна дължина
6. данни за auxv празнота* 48"
AT_NULL Elf64_auxv_t 16 {0,0}
...
auxv Elf64_auxv_t 16
7. auxv Elf64_auxv_t 16 Пр.: (0x0e,0x3e8)
НУЛА празнота* 8 0x00
...
околна среда въглен* 8
8. 0x7fffffffe308 околна среда въглен* 8 0x7fffffffe5e0
НУЛА празнота* 8 0x00
...
argv въглен* 8
9. 0x7fffffffe2f8 argv въглен* 8 0x7fffffffe5be
10. 0x7fffffffe2f0 argc дълго вътр 8" брой аргументи + 1
11. Локални променливи и аргументи, функции, извикани преди main
12. локални променливи основни
13. 0x7fffffffe1fc argc вътр 4 брой аргументи + 1
0x7fffffffe1f0 argv въглен** 8 0x7fffffffe2f8
0x7fffffffe1e8 околна среда въглен** 8 0x7fffffffe308
14. Променливи локални функции

„- Не намерих описания на нивите в документите, но те се виждат ясно в сметището.

За 32 бита не проверих, но най-вероятно е достатъчно просто да разделите размерите на две.

1. Достъпът до адреси над горната точка причинява Segfault.
2. Низ, съдържащ пътя до изпълнимия файл.
3. Масив от низове с променливи на средата
4. Масив от низове с опции на командния ред
5. Масив с произволна дължина. Изборът му може да бъде изключен с командите
sysctl -w kernel.randomize_va_space=0
echo 0 > /proc/sys/kernel/randomize_va_space
6. Данни за спомагателния вектор (например низът "x86_64")
7. Спомагателен вектор. Повече подробности по-долу.
8. Нулев терминален масив от указатели към низове на променливи на средата
9. Нулев терминален масив от указатели към низове с параметри на командния ред
10. Машинна дума, съдържаща броя на параметрите на командния ред (един от аргументите на "по-високите" функции, вижте т. 11)
11. Локални променливи и аргументи на функции, извикани преди main(_start,__libc_start_main..)
12. Променливи, декларирани в main
13. Основни аргументи на функцията
14. Променливи и аргументи на локални функции.

Спомагателен вектор
За i386 и x86_64 не можете да получите адреса на първия елемент от спомагателния вектор, но съдържанието на този вектор може да бъде получено по други начини. Един от тях е достъп до областта на паметта непосредствено след масива от указатели към низове на променливи на средата.
Трябва да изглежда нещо подобно:
#включи #включи int main(int argc, char** argv, char** env)( Elf64_auxv_t *auxv; //x86_64 // Elf32_auxv_t *auxv; //i386 while(*env++ != NULL); //намерете началото на спомагателния вектор за ( auxv = (Elf64_auxv_t *)env; auxv->a_type != AT_NULL; auxv++)( printf("addr: %p тип: %lx е: 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 копие: %d\n",*((int *)( argv - 1 )); върне 0; )
Структурите на Elf(32,64)_auxv_t са описани в /usr/include/elf.h. Функции за попълване на структури в linux-kernel/fs/binfmt_elf.c

Вторият начин за получаване на съдържанието на вектора:
hexdump /proc/self/auxv

Най-четливото представяне се получава чрез настройка променлива на средата LD_SHOW_AUXV.

LD_SHOW_AUXV=1л
AT_HWCAP: bfebfbff // възможности на процесора
AT_PAGESZ: 4096 //размер на страницата на паметта
AT_CLKTCK: 100 //честота на актуализиране пъти()
AT_PHDR: 0x400040 // информация за заглавката
AT_PHENT: 56
AT_PHNUM: 9
AT_BASE: 0x7fd00b5bc000 //адрес на интерпретатора, т.е. ld.so
AT_FLAGS: 0x0
AT_ENTRY: 0x402490 //входна точка на програмата
AT_UID: 1000 // идентификатори на потребители и групи
AT_EUID: 1000 //номинален и ефективен
AT_GID: 1000
AT_EGID: 1000
AT_SECURE: 0 // е зададеният флаг на setuid
AT_RANDOM: 0x7fff30bdc809 //Адрес 16 произволни байта,
генерирани при стартиране
AT_SYSINFO_EHDR: 0x7fff30bff000 //указател към страница, използвана за
//системни повиквания
AT_EXECFN: /bin/ls
AT_ПЛАТФОРМА: x86_64
Отляво е името на променливата, отдясно е стойността. Всички възможни имена на променливи и техните описания могат да бъдат намерени във файла elf.h. (константи с префикс AT_)

Връщане от main()
След инициализиране на контекста на процеса, управлението се прехвърля не към main(), а към функцията _start().
main() извиква вече от __libc_start_main. Това последна функцияима интересна функция - предава се указател към функция, която да се изпълни след main(). И този указател се предава естествено през стека.
Като цяло аргументите __libc_start_main имат формата според файла glibc-2.11/sysdeps/ia64/elf/start.S
/*
* Аргументи за __libc_start_main:
*изход0: основен
*изход1: argc
*изход2: argv
* out3: начален
* out4: fini //функция, извикана след main
* out5: rtld_fini
*out6: stack_end
*/
Тези. за да получите адреса на указателя fini, трябва да преместите две машинни думи от последната локална променлива main.
Ето какво се случи (работоспособността зависи от версията на компилатора):
#включи void **ret; празен *отпуск; void foo()( void (*boo)(void); //указател на функция printf("Stack rewrite!\n"); boo = (void (*)(void))leave; boo(); // fini ( ) ) int main(int argc, char *argv, char *envp) ( unsigned long int mark = 0xbfbfbfbfbfbfbfbf; // маркиране за работа от ret = (void**)(&mark+2); // извличане на адрес, функция извиква се след завършване (fini) leave = *ret; // съхранява *ret = (void*)foo; // презаписва return 0; // извиква foo() )

Надявам се да е било интересно.
Късмет.

Благодаря на потребителя Xeor за полезния съвет.

Когато създавате конзолно приложение на езика за програмиране C++, автоматично се създава ред, много подобен на този:

int main(int argc, char* argv) // параметри на функцията main().

Този ред е заглавката Главна функция main() , параметрите argс и argv са декларирани в скоби. Така че, ако програмата се изпълнява командна линия, тогава е възможно да се предаде някаква информация на тази програма, за това има параметри argc и argv . Параметърът argc е от тип данни int и съдържа броя на параметрите, предадени на Главна функция. Освен това argc винаги е поне 1, дори когато не подаваме никаква информация, тъй като името на функцията се счита за първия параметър. Параметърът argv е масив от указатели към низове. През командния ред могат да се предават само данни от тип низ. Указателите и низовете са две големи теми, за които са създадени отделни секции. Така че всяка информация се предава чрез параметъра argv. Нека разработим програма, която ще изпълняваме през командния ред. Windows линияи му предайте малко информация.

// argc_argv.cpp: Указва входната точка за конзолното приложение. #include "stdafx.h" #include използване на пространство от имена std; int main(int argc, char* argv) ( if (argc ><< argv<

// код Код::Блокове

// Dev-C++ код

// argc_argv.cpp: Указва входната точка за конзолното приложение. #включи използване на пространство от имена std; int main(int argc, char* argv) ( if (argc > 1)// ако предадем аргументи, тогава argc ще бъде по-голямо от 1 (в зависимост от броя на аргументите) ( cout<< argv<

След отстраняване на грешки в програмата, отворете командния ред на Windows и плъзнете изпълнимия файл на нашата програма в прозореца на командния ред, пълният път до програмата ще бъде показан на командния ред (но можете да напишете пътя до програмата ръчно), след които можете да натиснете ENTERи програмата ще стартира (вижте Фигура 1).

Фигура 1 - Параметри на основната функция

Тъй като току-що стартирахме програмата и не й подадохме никакви аргументи, се появи съобщението Not arguments. Фигура 2 показва стартирането на същата програма през командния ред, но с подаден към нея аргумент Open.

Фигура 2 - Параметри на основната функция

Аргументът е думата Open, както можете да видите от фигурата, тази дума се появи на екрана. Можете да подадете няколко параметъра наведнъж, като ги разделите със запетая. Ако е необходимо да се предаде параметър, състоящ се от няколко думи, тогава те трябва да бъдат затворени в двойни кавички и тогава тези думи ще се считат за един параметър. Например, фигурата показва стартирането на програмата, предавайки й аргумент, състоящ се от две думи - It work .

Фигура 3 - Параметри на основната функция

И ако махнете кавичките. Тогава ще видим само думата То. Ако не планирате да предавате информация при стартиране на програмата, тогава можете да премахнете аргументите във функцията main(), можете също да промените имената на тези аргументи. Понякога има модификации на параметрите argc и argv, но всичко зависи от типа на създаваното приложение или от средата за разработка.

Незадължителни и именувани аргументи

Незадължителни аргументи

C# 4.0 въвежда нова функция, която подобрява удобството при посочване на аргументи при извикване на метод. Този инструмент се нарича незадължителни аргументии ви позволява да дефинирате стойност по подразбиране за параметър на метод. Тази стойност ще се използва по подразбиране, ако не е зададен съответен аргумент за параметъра, когато методът се извиква. Следователно не е необходимо да се указва аргумент за такъв параметър. Незадължителните аргументи улесняват извикването на методи, където аргументите по подразбиране се прилагат към някои параметри. Те могат да се използват и като "кратка" форма на претоварване на метода.

Основният импулс за добавяне на незадължителни аргументи беше необходимостта да се опрости взаимодействието с COM обекти. Няколко обектни модела на Microsoft (например Microsoft Office) предоставят функционалност чрез COM обекти, много от които са написани отдавна и са проектирани да използват незадължителни параметри.

Пример за използване на незадължителни аргументи е показан по-долу:

Използване на системата; използване на System.Collections.Generic; използване на System.Linq; използване на System.Text; namespace ConsoleApplication1 ( class Program ( // Аргументите b и c са незадължителни при извикване на 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(); ) ) )

Трябва да се има предвид, че всички незадължителни аргументи трябва задължително да бъдат посочени вдясно от задължителните. В допълнение към методите незадължителните аргументи могат да се използват в конструктори, индексатори и делегати.

Едно предимство на незадължителните аргументи е, че те улесняват програмиста да се справя със сложни извиквания на методи и конструктори. В края на краищата често е необходимо да се зададат повече параметри в метода, отколкото обикновено се изисква. И в случаи като този, някои от тези параметри могат да бъдат направени незадължителни чрез внимателно използване на незадължителните аргументи. Това означава, че трябва да бъдат предадени само тези аргументи, които са важни в конкретния случай, а не всички аргументи, които иначе би трябвало да се изискват. Този подход ни позволява да рационализираме метода и да опростим работата на програмиста с него.

Наименувани аргументи

Друга функция, която беше добавена към C# с излизането на .NET 4.0, е поддръжката на т.нар именувани аргументи. Както знаете, когато подавате аргументи към метод, редът, в който се появяват, по правило трябва да съвпада с реда, в който параметрите са дефинирани в самия метод. С други думи, стойността на аргумента се присвоява на параметъра чрез неговата позиция в списъка с аргументи.

Именуваните аргументи са предназначени да преодолеят това ограничение. Наименуван аргумент ви позволява да посочите името на параметъра, на който е присвоена неговата стойност. И в този случай редът на аргументите вече няма значение. По този начин именуваните аргументи са донякъде подобни на инициализаторите на обекти, споменати по-рано, въпреки че се различават от тях по своя синтаксис. За да посочите аргумент по име, използвайте следната форма на синтаксис:

име_на_параметър: стойност

Тук име_на_параметъробозначава името на параметъра, към който се предава стойността. Разбира се, parameter_name трябва да е името на валиден параметър за метода, който се извиква.

Можете да предавате определени аргументи на C програми. Когато main() се извика в началото на изчислението, към него се предават три параметъра. Първият от тях определя броя на аргументите на командата при достъп до програмата. Вторият е масив от указатели към символни низове, съдържащи тези аргументи (по един аргумент на низ). Третият също е масив от указатели към символни низове, той се използва за достъп до параметрите на операционната система (променливи на средата).

Всяка такава линия се представя като:

променлива = стойност\0

Последният ред може да бъде намерен от две нули в края.

Нека именуваме съответно аргументите на функцията main(): argc, argv и env (възможни са всякакви други имена). След това са разрешени следните описания:

main(int argc, char *argv)

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

Да предположим, че има някаква програма prog.exe на устройство A:. Нека го адресираме така:

A:\>prog.exe файл1 файл2 файл3

Тогава argv е указател към низа A:\prog.exe, argv е указател към низа file1 и т.н. Първият действителен аргумент се сочи от argv, а последният от argv. Ако argc=1, тогава няма параметри след името на програмата в командния ред. В нашия пример argc=4.

рекурсия

Рекурсията е метод за извикване, при който функция извиква сама себе си.

Важен момент при съставянето на рекурсивна програма е организацията на изхода. Лесно е да направите грешката тук, че функцията последователно ще се самоизвиква за неопределено време. Следователно рекурсивният процес трябва стъпка по стъпка да опрости проблема, така че в крайна сметка да се появи нерекурсивно решение за него. Използването на рекурсия не винаги е желателно, тъй като може да доведе до препълване на стека.

Функции на библиотеката

В системите за програмиране подпрограмите за решаване на общи проблеми се комбинират в библиотеки. Тези задачи включват: изчисляване на математически функции, въвеждане/извеждане на данни, обработка на низове, взаимодействие с инструменти на операционната система и др. Използването на библиотечни рутинни процедури освобождава потребителя от необходимостта да разработва подходящи инструменти и му предоставя допълнителна услуга. Функциите, включени в библиотеките, се доставят със системата за програмиране. Техните декларации са дадени в *.h файлове (това са така наречените включващи или заглавни файлове). Следователно, както бе споменато по-горе, в началото на програмата с библиотечни функции трябва да има редове като:

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

Например:

#включи

Има и възможности за разширяване и създаване на нови библиотеки с потребителски програми.

На глобалните променливи се присвоява фиксирано място в паметта за продължителността на програмата. Локалните променливи се съхраняват в стека. Между тях има област на паметта за динамично разпределение.

Функциите malloc() и free() се използват за динамично разпределяне на свободна памет. Функцията malloc() заделя памет, функцията free() я освобождава. Прототипите на тези функции се съхраняват в заглавния файл stdlib.h и изглеждат така:

void *malloc(размер_t размер);

void *free(void *p);

Функцията malloc() връща указател от тип void; за правилна употреба стойността на функцията трябва да се преобразува в указател към съответния тип. При успех функцията връща указател към първия байт свободна памет с размер size. Ако няма достатъчно памет, се връща стойността 0. Операцията sizeof() се използва за определяне на броя байтове, необходими за променлива.

Пример за използване на тези функции:

#включи

#включи

p = (int *) malloc(100 * sizeof(int)); /* Разпределяне на памет за 100

цели числа */

printf("Няма памет\n");

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

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

безплатно (p); /* Свободна памет */

Преди да използвате указателя, върнат от malloc(), трябва да се уверите, че има достатъчно памет (указателят не е нула).

Препроцесор

C препроцесорът е програма, която обработва вход към компилатор. Препроцесорът преглежда изходната програма и извършва следните действия: свързва дадените файлове към нея, извършва замествания и също така управлява условията за компилиране. Препроцесорът е предназначен за програмни редове, които започват със символа #. Само една команда (препроцесорна директива) е разрешена на ред.

Директива

#define заместване на идентификатор

кара следния програмен текст да замени посочения идентификатор с текста за заместване (обърнете внимание на липсата на точка и запетая в края на тази команда). По същество тази директива въвежда дефиниция на макрос (макро), където "идентификатор" е името на дефиницията на макрос, а "заместване" е последователността от знаци, с които препроцесорът замества указаното име, когато го намери в текста на програмата. Името на макроса обикновено се изписва с главни букви.

Помислете за примери:

Първият ред кара програмата да замени идентификатора MAX с константата 25. Вторият ви позволява да използвате в текста вместо отварящата фигурна скоба (() думата BEGIN.

Обърнете внимание, че тъй като препроцесорът не проверява съвместимостта между символните имена на макро дефинициите и контекста, в който се използват, се препоръчва такива идентификатори да се дефинират не с директивата #define, а с ключовата дума const с явен тип индикация (това е по-вярно за C++):

const int MAX = 25;

(типът int може да бъде пропуснат, тъй като е зададен по подразбиране).

Ако директивата #define изглежда така:

#define идентификатор (идентификатор, ..., идентификатор) заместване

и няма интервал между първия идентификатор и отварящата скоба, тогава това е дефиниция на макро заместване с аргументи. Например след появата на ред като:

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

оператор READ(y); се третира по същия начин като scanf("%d",&y);. Тук val е аргумент и макрозаместването се извършва с аргумента.

Ако в замяната има дълги дефиниции, които продължават на следващия ред, знак \ се поставя в края на следващия продължаващ ред.

Можете да поставите обекти, разделени с ## символи в дефиниция на макрос, например:

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

След това PR(a, 3) ще извика заместването a3. Или, например, дефиниция на макрос

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

ще промени z(sin, x, +, y) на sin(x+y).

Знакът #, поставен преди аргумент на макрос, показва, че той е преобразуван в низ. Например след директивата

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

следния фрагмент от програмния текст

се преобразува така:

printf("година""= %d", година);

Нека опишем други директиви на препроцесора. Директивата #include е била виждана и преди. Може да се използва в две форми:

#include "име на файл"

#включи<имя файла>

Ефектът и от двете команди е включване на файлове с посоченото име в програмата. Първият зарежда файл от текущата директория или директорията, посочена като префикс. Втората команда търси файла в стандартни местоположения, определени в системата за програмиране. Ако файлът, чието име е написано в двойни кавички, не бъде намерен в посочената директория, тогава търсенето ще продължи в поддиректориите, посочени за командата #include<...>. Директивите #include могат да бъдат вложени една в друга.

Следващата група директиви ви позволява да компилирате избирателно части от програмата. Този процес се нарича условна компилация. Тази група включва директивите #if, #else, #elif, #endif, #ifdef, #ifndef. Основната форма на директивата #if е:

#if постоянен_израз_изявление_последователност

Тук се проверява стойността на константния израз. Ако е вярно, тогава се изпълнява дадената последователност от оператори, а ако е невярно, тогава тази последователност от оператори се пропуска.

Действието на директивата #else е подобно на действието на командата else в езика C, например:

#if постоянен_израз

изявление_последователност_2

Тук, ако константният израз е верен, тогава се изпълнява последователност_от_оператори_1, а ако е невярно, се изпълнява последователност_от_оператори_2.

Директивата #elif означава действие тип "друго ако". Основната форма на неговото използване е следната:

#if постоянен_израз

последователност_изявления

#elif постоянен_израз_1

изявление_последователност_1

#elif постоянен_израз_n

последователност_от_изказвания_n

Тази форма е подобна на конструкцията на езика C на формата: if...else if...else if...

Директива

#ifdef идентификатор

задава дали зададеният идентификатор е дефиниран в момента, т.е. дали е включено в директиви във формата #define. Преглед на линията

#ifndef идентификатор

проверява дали зададеният идентификатор в момента е недефиниран. Всяка от тези директиви може да бъде последвана от произволен брой редове текст, евентуално съдържащ #else израз (#elif не може да се използва) и завършващ с ред #endif. Ако условието, което се проверява, е вярно, тогава всички редове между #else и #endif се игнорират, а ако е невярно, тогава редовете между проверката и #else (ако няма дума #else, тогава #endif). Директивите #if и #ifndef могат да бъдат вложени една в друга.

Преглед на директива

#undef идентификатор

кара посоченият идентификатор да се счита за недефиниран, т.е. не подлежи на смяна.

Разгледайте примери. Трите директиви са:

проверете дали идентификаторът WRITE е дефиниран (т.е. беше команда във формата #define WRITE...) и ако е така, тогава името WRITE се счита за недефинирано, т.е. не подлежи на смяна.

директиви

#define WRITE fprintf

проверете дали идентификаторът WRITE е недефиниран и ако е така, тогава идентификаторът WRITE се определя вместо името на fprintf.

Директивата #error е написана в следната форма:

#error грешка_съобщение

Ако се появи в текста на програмата, компилирането спира и на екрана на дисплея се показва съобщение за грешка. Тази команда се използва главно по време на фазата на отстраняване на грешки. Имайте предвид, че съобщението за грешка не е необходимо да бъде оградено в двойни кавички.

Директивата #line има за цел да промени стойностите на променливите _LINE_ и _FILE_, дефинирани в системата за програмиране C. Променливата _LINE_ съдържа номера на реда на програмата, която се изпълнява в момента. Идентификаторът _FILE_ е указател към низ с името на програмата, която се компилира. Директивата #line е написана както следва:

номер на #ред "име на файл"

Тук числото е всяко положително цяло число, което ще бъде присвоено на променливата _LINE_, името на файла е незадължителен параметър, който заменя стойността на _FILE_.

Директивата #pragma ви позволява да предадете някои инструкции на компилатора. Например линията

показва, че има низове на асемблер в C програма. Например:

Помислете за някои глобални идентификатори или имена на макроси (имена на дефиниции на макроси). Дефинирани са пет такива имена: _LINE_, _FILE_, _DATE_, _TIME_, _STDC_. Два от тях (_LINE_ и _FILE_) вече са описани по-горе. Идентификаторът _DATE_ указва низ, който съхранява датата, на която изходният файл е преведен в обектен код. Идентификаторът _TIME_ указва низ, който съхранява времето, когато изходният файл е преведен в обектен код. Макросът _STDC_ има стойност 1, ако се използват стандартно дефинирани имена на макроси. В противен случай тази променлива няма да бъде дефинирана.


Понякога при стартиране на програма е полезно да й се предаде някаква информация. Обикновено тази информация се предава на функцията main() чрез аргументи на командния ред. Аргумент на командния реде информация, която се въвежда в командния ред на операционната система след името на програмата. Например, за да започнете да компилирате програма, трябва да въведете следното в командния ред след подканата:

CC име_на_програма

име_на_програмае аргумент от командния ред, който указва името на програмата, която ще компилирате.

За приемане на аргументи от командния ред се използват два специални вградени аргумента: argc и argv. Параметърът argc съдържа броя на аргументите в командния ред и е цяло число и винаги е поне 1, тъй като първият аргумент е името на програмата. А параметърът argv е указател към масив от указатели към низове. В този масив всеки елемент сочи към някакъв аргумент от командния ред. Всички аргументи на командния ред са низове, така че конвертирането на произволни числа в желания двоичен формат трябва да бъде предоставено в програмата, когато е разработена.

Ето прост пример за използване на аргумента на командния ред. Екранът показва думата Hello и вашето име, което трябва да бъде посочено като аргумент на командния ред.

#включи #включи int main(int argc, char *argv) ( if(argc!=2) ( printf("Забравихте да въведете името си.\n"); exit(1); ) printf("Здравейте %s", argv) ; върне 0; )

Ако сте нарекли името на тази програма (име) и вашето име е Том, тогава, за да стартирате програмата, въведете името Том в командния ред. В резултат на стартиране на програмата на екрана ще се появи съобщението Hello, Tom.

В много среди всички аргументи на командния ред трябва да бъдат разделени един от друг с интервал или раздел. Запетаите, точката и запетаята и подобни знаци не се считат за разделители. Например,

Бягай Спот, бягай

се състои от три символни низа, докато

Ерик, Рик, Фред

е низ от един знак - запетаите обикновено не се считат за разделители.

Ако низът съдържа интервали, тогава в някои среди низът може да бъде ограден в двойни кавички, за да се предотврати създаването на множество аргументи. В резултат на това целият низ ще се счита за един аргумент. За да научите повече за това как са зададени опциите на командния ред във вашата операционна система, вижте документацията за тази система.

Много е важно да декларирате правилно argv. Ето как го правят най-често:

Char *argv;

Празните квадратни скоби показват, че масивът е с неопределена дължина. Вече можете да получите достъп до отделни аргументи чрез индексиране на масива argv. Например argv сочи към първия символен низ, който винаги е името на програмата; argv сочи към първия аргумент и т.н.

Друг малък пример за използване на аргументи на командния ред е програмата за обратно броене по-долу. Тази програма отброява обратно от някаква стойност (посочена в командния ред) и издава звуков сигнал, когато достигне 0. Обърнете внимание, че първият аргумент, съдържащ първоначалната стойност, се преобразува в целочислена стойност с помощта на стандартната функция atoi (). Ако вторият аргумент на командния ред (и ако вземем името на програмата като трети аргумент) е низът "дисплей" (извежда се на екрана), тогава резултатът от обратното броене (в обратен ред) ще бъде показан на екрана.

/* Програма за обратно броене. */ #включи #включи #включи #включи 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; }

Имайте предвид, че ако аргументите на командния ред не са посочени, ще се покаже съобщение за грешка. Програмите с аргументи на командния ред често правят следното: когато потребителят стартира тези програми, без да въведе необходимата информация, се показват инструкции как правилно да се уточнят аргументите.

За достъп до един знак в един от аргументите на командния ред, въведете втория индекс в argv. Например, следната програма отпечатва символ по знак всички аргументи, с които е била извикана:

#включи int main(int argc, char *argv) ( int t, i; for(t=0; t

Не забравяйте, че първият индекс на argv осигурява достъп до низа, а вторият индекс осигурява достъп до неговите отделни знаци.

Обикновено argc и argv се използват за предаване на първоначалните команди към програмата, които ще са й необходими при стартиране. Например аргументите на командния ред често указват данни като име на файл, опция или алтернативно поведение. Използването на аргументи на командния ред придава на вашата програма "професионален вид" и я прави по-лесна за използване в пакетни файлове.

Имената argc и argv са традиционни, но не са задължителни. Можете да наименувате тези два параметъра във функцията main() както желаете. Освен това някои компилатори може да поддържат -допълнителни аргументи за main(), така че не забравяйте да проверите документацията за вашия компилатор.

Когато една програма не изисква параметри на командния ред, най-често е изрично да се декларира функцията main() да няма параметри. В този случай ключовата дума void се използва в списъка с параметри на тази функция.