Программирование на С

C — это язык программирования общего назначения, который широко используется и стал основой многих языков программирования высокого уровня, которые доминируют в профессии программиста, таких как Java, Javascript, Python, C++, C# и другие.

Недавно я начал учиться кодировать на C и заметил несколько вещей. Как человек, который изначально начинал с Python, вам нужно выполнить несколько шагов, чтобы ваш код заработал. Эти шаги известны как компиляция, когда один набор компьютерного кода, написанного на одном языке программирования, преобразуется в другой, чтобы он мог работать.

Для C этот процесс представляет собой преобразование кода C, который может быть понят людьми, и преобразование его в код сборки, который может быть понят компьютерами для запуска программ. Этот процесс можно разделить на четыре этапа: предварительная обработка, компиляция, сборка и связывание.

Давайте изучим этот процесс на примере кода C main.c.

Представьте, что мы написали простую программу с именем main.c, которая просто выводит Hello World. Существует много разных типов компиляторов, но для нашего использования давайте воспользуемся GCC или GNU Compiler Collection (https://gcc.gnu.org/)

#include <stdio.h>
int main(void)
{
        printf("Hello World\n");
        return(0);
}

Предварительная обработка

Предварительная обработка — это первый шаг компиляции, на котором препроцессор выполняет некоторую начальную обработку, чтобы компиляция прошла гладко. Процесс начинается с заголовка, который обычно находится в верхней части файла кода и начинается с #. Для стандартных операций ввода-вывода обычно вы видите #include ‹stdio.h›, который указывает, что ваш код будет иметь некоторые входы и выходы. Затем препроцессор продолжит работу и начнет объединять строки и опускать комментарии.

Вы можете видеть, что на самом деле можете взглянуть на то, что выводит препроцессор, используя команду ниже:

gcc -E main.c

Вывод будет выглядеть примерно так:

[There are a bunch of lines above here but I left it out for the sake of space]
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 943 "/usr/include/stdio.h" 3 4
# 2 "main.c" 2
int main(void)
{
 printf("Hello World\n");
 return(0);
}

Подборка

Затем у нас есть второй этап, который также называется компиляцией (не весь процесс). Именно на этом этапе исходный код из main.c транслируется в ассемблерный код, который по-прежнему читается человеком, и является промежуточным шагом к созданию объектного кода, который будет считываться компьютером.

Вы можете посмотреть, что выводится на этом этапе, используя приведенную ниже команду, которая создаст файл с именем main.s:

gcc -S main.c

Если бы мы открыли main.s, это выглядело бы так:

.file   "main.c"
        .section        .rodata
.LC0:
        .string "Hello World"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $.LC0, %edi
        call    puts
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4"
        .section        .note.GNU-stack,"",@progbits

Сборка

Это она, инструкции по сборке затем обрабатываются ассемблером, где он выводит фактические инструкции, данные компьютеру для запуска программы.

Опять же, мы можем взглянуть на то, как это выглядит, используя команду ниже:

gcc -c main.c

Затем эта команда создаст файл с именем main.o, содержимое которого будет выглядеть примерно так:

^@UH<89>å¿^@^@^@^@è^@^@^@^@¸^@^@^@^@]ÃHello World^@^@GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4^@^@^@^@^T^@^@^@^@^@^@^@^AzR^@^Ax^P^A^[^L^G^H<90>^A^@^@^\^@^@^@^\^@^@^@^@^@^@^@^U^@^@^@^@A^N^P<86>^BC^M^FP^L^G^H^@^@^@^@.symtab^@.strtab^@.shstrtab^@.rela.text^@.data^@.bss^@.rodata^@.comment^@.note.GNU-stack^@.rela.eh_frame

Много тарабарщины, но это то, что компьютер читает, как инструкции по запуску нашей программы!

Связывание

Последним шагом будет связывание, которое возьмет объектный код или машинные инструкции, сгенерированные на этапе сборки, и создаст исполняемую программу, которая затем запустит программу.

Если бы мы запустили компилятор в main.c, он по умолчанию использовал бы файл a.out, но мы можем переименовать его в исполняемый файл main это с помощью команды ниже:

gcc -o main main.c

При этом у нас есть исполняемый файл с именем main, который мы можем использовать для запуска нашей программы, main.c.

Теперь мы можем понять шаги, необходимые для перехода от написания кода C к созданию исполняемого файла, который позволит нам запустить программу. Хотя я показал шаг за шагом компиляцию программы, чтобы показать вам, что происходит, обычно все это происходит с помощью последней команды.

Надеюсь, вам понравилась эта статья, и вы подпишитесь на меня в Твиттере, чтобы узнать больше тем в ближайшие месяцы!

https://twitter.com/rmiyazaki3