Int main c что это

от admin

Язык С: структура программы

Программа на языке C состоит из нескольких частей. Вот основные из них:

  • команды препроцессора;
  • функции;
  • переменные;
  • операторы и выражения;
  • комментарии.

Взгляните на простой код, который будет печатать слова “Hello World”:

Рассмотрим каждую часть приведенной выше программы:

  • Первая строка программы #include <stdio.h> — это команда препроцессора, которая указывает компилятору языка C включить файл stdio.h, прежде чем приступить к компиляции.
  • Следующая строка int main() — это главная функция, с которой начинается выполнение программы.
  • Следующая строка /*…*/ будет проигнорирована компилятором. Она потребовалась для добавления дополнительных комментариев в программу. Поэтому такие строки называются комментариями в программе.
  • Следующая строка printf(…) — это еще одна функция, доступная в C, которая вызывает вывод на экран сообщения “Hello, World!”.
  • Следующая строка return 0 завершает функцию main() и возвращает значение 0 .

Компиляция и выполнение программы

Теперь посмотрим, как сохранить исходный код в файле, а также как его скомпилировать и запустить:

Int main C: для чего нужна функция main, прототип в функции Cи

«Int main()» — это выражение, показыва ющее , что в программе присутствует главная функция main(), которая вернет в качестве значения целое число. В программе на С может быть несколько функций, но одна из них обязательно должна быть main(). Без нее программа не будет корректно работать, так как main() является управляющей и основной функцией.

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Несколько подробностей о функции main

Однажды заинтересовался, содержимым стека функции main процесса в linux. Провел некоторые изыскания и теперь представляю вам результат.

Варианты описания функции main:
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 — нуль-терминальный массив указателей на строки переменных окружения. Каждая строка в формате ИМЯ=ЗНАЧЕНИЕ
auxv — массив вспомогательных значение (доступно только для PowerPC [1])
apple — путь к исполняемому файлу (в MacOS и Darwin [2])
Вспомогательный вектор — массив с различной дополнительной информацией, такой как эффективный идентификатор пользователя, признак setuid бита, размер страницы памяти и т.п.

Далее о том как получить массив вспомогательных значений для i386 и x86_64, а также об остальном содержимом «сегмента» стека.

Размер сегмента стека можно глянуть в файле maps:
cat /proc/10918/maps

7ffffffa3000-7ffffffff000 rw-p 00000000 00:00 0 [stack]

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

1. 0x7ffffffff000 Верхняя точка сегмента стека. Обращение вызывает segfault
0x7ffffffff0f8 NULL void* 8 0x00′
2. filename[0] char 1+ «/tmp/a.out»
char 1 0x00
.
env[1][0] char 1 0x00
.
char 1 0x00
3. 0x7fffffffe5e0 env[0][0] char 1 ..
char 1 0x00
.
argv[1][0] char 1 0x00
.
char 1 0x00
4. 0x7fffffffe5be argv[0][0] char 1+ «/tmp/a.out»
5. Массив случайной длины
6. данные для auxv void*[] 48′
AT_NULL Elf64_auxv_t 16
.
auxv[1] Elf64_auxv_t 16
7. auxv[0] Elf64_auxv_t 16 Ex.:
NULL void* 8 0x00
.
env[1] char* 8
8. 0x7fffffffe308 env[0] char* 8 0x7fffffffe5e0
NULL void* 8 0x00
.
argv[1] char* 8
9. 0x7fffffffe2f8 argv[0] char* 8 0x7fffffffe5be
10. 0x7fffffffe2f0 argc long int 8′ число аргументов + 1
11. Локальные переменные и аргументы, функций вызываемых до main
12. Локальные переменные main
13. 0x7fffffffe1fc argc int 4 число аргументов + 1
0x7fffffffe1f0 argv char** 8 0x7fffffffe2f8
0x7fffffffe1e8 env char** 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.Аргументы функции main
14.Переменные и аргументы локальных функций.

Вспомогательный вектор
Для i386 и x86_64 нельзя получить адрес первого элемента вспомогательного вектора, однако содержимое этого вектора можно получить другими способами. Один из них — обратиться к области памяти, лежащей сразу за массивом указателей на строки переменных окружения.
Это должно выглядеть примерно так:

Структуры 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 ls
AT_HWCAP: bfebfbff //возможности процессора
AT_PAGESZ: 4096 //размер страницы памяти
AT_CLKTCK: 100 //частота обновления times()
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_PLATFORM: x86_64
Слева — название переменной, справа значение. Все возможные названия переменных и их описание можно глянуть в файле elf.h. (константы с префиксом AT_)

Возвращение из main()
После инициализации контекста процесса управление передается не в main(), а в функцию _start().
main() вызывает уже из __libc_start_main. Эта последняя функция имеет интересную особенность — ей передается указатель на функцию, которая должна быть выполнена после main(). И указатель этот передается естественно через стек.
Вообще аргументы __libc_start_main имеют вид, согласно файла glibc-2.11/sysdeps/ia64/elf/start.S
/*
* Arguments for __libc_start_main:
* out0: main
* out1: argc
* out2: argv
* out3: init
* out4: fini //функция вызываемая после main
* out5: rtld_fini
* out6: stack_end
*/
Т.е. чтобы получить адрес указателя fini нужно сместиться на два машинных слова от последней локальной переменной main.
Вот что получилось(работоспособность зависит от версии компилятора):

How does the main() method work in C?

In C++ I know we can overload a method, but in C how does the compiler handle these two different signatures of main function?

9 Answers 9

Some of the features of the C language started out as hacks which just happened to work.

Multiple signatures for main, as well as variable-length argument lists, is one of those features.

Programmers noticed that they can pass extra arguments to a function, and nothing bad happens with their given compiler.

This is the case if the calling conventions are such that:

  1. The calling function cleans up the arguments.
  2. The leftmost arguments are closer to the top of the stack, or to the base of the stack frame, so that spurious arguments do not invalidate the addressing.

One set of calling conventions which obeys these rules is stack-based parameter passing whereby the caller pops the arguments, and they are pushed right to left:

In compilers where this type of calling convention is the case, nothing special need to be done to support the two kinds of main , or even additional kinds. main can be a function of no arguments, in which case it is oblivious to the items that were pushed onto the stack. If it’s a function of two arguments, then it finds argc and argv as the two topmost stack items. If it’s a platform-specific three-argument variant with an environment pointer (a common extension), that will work too: it will find that third argument as the third element from the top of the stack.

And so a fixed call works for all cases, allowing a single, fixed start-up module to be linked to the program. That module could be written in C, as a function resembling this:

In other words, this start module just calls a three-argument main, always. If main takes no arguments, or only int, char ** , it happens to work fine, as well as if it takes no arguments, due to the calling conventions.

If you were to do this kind of thing in your program, it would be nonportable and considered undefined behavior by ISO C: declaring and calling a function in one manner, and defining it in another. But a compiler’s startup trick does not have to be portable; it is not guided by the rules for portable programs.

But suppose that the calling conventions are such that it cannot work this way. In that case, the compiler has to treat main specially. When it notices that it’s compiling the main function, it can generate code which is compatible with, say, a three argument call.

That is to say, you write this:

But when the compiler sees it, it essentially performs a code transformation so that the function which it compiles looks more like this:

except that the names __argc_ignore don’t literally exist. No such names are introduced into your scope, and there won’t be any warning about unused arguments. The code transformation causes the compiler to emit code with the correct linkage which knows that it has to clean up three arguments.

Another implementation strategy is for the compiler or perhaps linker to custom-generate the __start function (or whatever it is called), or at least select one from several pre-compiled alternatives. Information could be stored in the object file about which of the supported forms of main is being used. The linker can look at this info, and select the correct version of the start-up module which contains a call to main which is compatible with the program’s definition. C implementations usually have only a small number of supported forms of main so this approach is feasible.

Compilers for the C99 language always have to treat main specially, to some extent, to support the hack that if the function terminates without a return statement, the behavior is as if return 0 were executed. This, again, can be treated by a code transformation. The compiler notices that a function called main is being compiled. Then it checks whether the end of the body is potentially reachable. If so, it inserts a return 0;

Читать:
Как определить является ли граф планарным

Похожие статьи