# 02 лекция Полная версия ```с #include <stdio.h> int sum(int a, int b) { return a + b; } int main(void) { int a=3, b=5; printf("a+b=%d\n", sum(a, b)); return 0; } ``` Так не скомпилируется (ниже) ```с #include <stdio.h> int main(void) { int a=3, b=5; printf("a+b=%d\n", sum(a, b)); return 0; } int sum(int a, int b) { return a + b; } ``` :::warning -Что нужно сделать, чтобы оно работало? :::spoiler Ответ -Нужно сообщить компилятору, что она существует, по факту написать прототип функции (обьявить функцию) ::: ```с #include <stdio.h> int sum(int, int); int main(void) { int a=3, b=5; printf("a+b=%d\n", sum(a, b)); return 0; } int sum(int a, int b) { return a + b; } ``` В этом случае можно расположить реализацию функции после вызова. Но тогда нам нужно прописывать прототипы всех стандартных функций, это слишком долго и муторно, по этоиу мы подлючаем стандартные заголовочные файлы, в который и прописаны это прототипы. например для ввода вывода: `#include <stdio.h>` :::info -обязательно ли писать имена аргументов в обьявлении? :::spoiler Ответ -нет.\ (Не знаю, про что это - пока обязательно, в 23 возможно поправят) ::: --- **Как можно написать реализацию функции в другом файле?** :::warning > [color=#B22222] sum.c ```с int sum(int a, int b) // можно релизовать в этом файле { return a + b; } ``` > [color=#32CD32] main.c ```с #include <stdio.h> int sum(int a, int b); // если обьявить int main(void) { int a=3, b=5; printf("a+b=%d\n", sum(a, b)); // можно использовать return 0; } ``` ::: **Когда есть прототип, функция может быть реализованная не только после, но и в другом файле** ИЛИ :::success > [color=#B22222] sum.c ``` int sum(int a, int b) // можно релизовать в этом файле { return a + b; } ``` > [color=#4682B4] sum.h ``` int sum(int a, int b); // можно обьявить в этом файле ``` > [color=#32CD32] main.c ``` #include <stdio.h> #include "sum.h" // можно подключить заголовочный файл // "" пишится вместо <> тогда, когда у вас подключаемый файл находится в текущей рабочей папке int main(void) { int a=3, b=5; printf("a+b=%d\n", sum(a, b)); // можно использовать return 0; } ``` :::success При компиляции `#include <>` подключаемый файл сначала ищется в стандартных каталогах, а потом в текущей рабочей папке При компиляции `#include ""` подключаемый файл ищется только в текущем рабочем каталоге *текущая рабочая папка, в данном случае, откуда запустили компилятор ::: >текущая рабочая папка в остальных случаях (в других контекстах, например при открытие файла) - место откуда запустили ваш `.exe` (исполяемый файл, `.exe` - это расширение исполняемых файлов под `windows`) --- ## Компиляция Как всё это работает? Обычно, как во многих языках, процесс компиляции состоит из *двух* этапов: 1. Компиляция - во время процесса получаются обьектные файлы расширения `.o` или `.obj` обычно, компилируются только файлы `.c` и независимо друг от друга. 2. Линковка - получается `.exe` (если win), он собирает всю программу вместе, и вставляет нужные куски из других файлов на место недопереведенных. > Компиляция начинается с предпроцессора `.obj` - полуфабрикат, где часть кода переведенна в машинный код, а кое что ещё недопереведенно, например недопереведенно будет вызов функции `sum` в `main` в последнем примере выше. Если мы обьявим функцию но не реализуем её нигде? Программа скомпилируется, но не слинкуется. (или окажется несколько функций с одинаковым именем в нескольких файлах, должны совпасть имена функций и типы принимаемые аргументы) (или если нет `main`) На самом деле выполнение работы начинается не с `main`, а с `runtime`, служебных си'шных функций, которые инициализуют окружение (например парсят аргументы `main`, иниц глобальные аргументы), а он уже вызывает `main`. > Старт программы - недра системы->runtime C->main > Точка входа - main Линковка может работать с разными языками, ей вообще не важен язык. :::danger -Во время линковки мы компилируем файлы из одной директории (которые лежат в одной папке)? **-Нет, во время линковки мы компилируем и собираем вместе то, что мы дали линковщику** ::: #### Про DLL `.dll` - это тоже самое, что и `.exe`, но без точки входа (что-то экспортировать), призваны для уменьшения времени компиляции, и для сокрытия исходного кода. #### Про документацию: :::info Рекомендация - читать документацию на английском Программисты пишут документацию на английском, на других языках её очень лень писать, и очень часто перевод документации - машинный, или каким нибудь гуманитарием. По этому смысл в такой документации очень искажен. (тоже самое про [ru.wikipedia.org](https://ru.wikipedia.org/)) ::: #### Про поисковики: :::info Девиз Яндекса - найдется всё (зарос рекламой и ведёт себя неприлично) Девиз Google - найдётся то, что ты искал ::: --- ### LTCG ***Link Time Code Generation*** Смысл - оптимизировать всю программу. Компилятор видит каждый файл программы **НЕзависимо**, поэтому что-то съоптимизировать между файлами просто принципиально не может. Если мы хотим дать возможность съоптимизировать программу целиком - применяется специальный хитрый вариант компиляции, когда `.obj` дает не машинный код, а промежуточный код этого компилятора, а потом линкер на этапе линковки снова вызывает компилятор, уже для всего общего промежуточного кода, и он его докомпилирует до машинного. Для этого линкер и компилятор должны 'дружить'. **Name Mangling**\ Проблема в том, что на самом деле имя у этих функций не одинаковое, и когда всё это компилируется в `.obj` линкер без понятия плюсы там или нет и т.д. Если вы линкеру дадите несколько функций с одинаковым именем - он вас отругает. По этому ++ этим функциям дают неодинаковые имена, они к имени функции добавляют всякие @? и т.д. где этими магическими символами кодируют типы аргументов этих функции, то есть на самом деле имена этих функций уникальные, просто с добавкой. По этому если мы создадим один файл .c, а другой .cpp то у нас не скомпилируется, тк у одной и той же функции будет раные имена (в си++ туда добавятся магические символы) и линковщиу не сможет найти реализацию вызываемой функции. **Это лечится**, если сказать компилятору C++ не трогать имя, я хочу настоящее имя функции - `extern "C" void f (int x);`, но тогда перегрузить функцию `f` не получится. В стандарте **Name Mangling** не описан, и предугадать его не получится никак, тк даже один компилятор в разных версиях использует разные генерации этих имен (совместимость на двоичном уровне у ++ гораздо хуже чем у Си) > Мы не хотим компилировать разные части разными компиляторами #### Ещё одно значение си Си выбран стыкующим языком, если мы хотим пристыковать к одному языку код на другом языке - то эта штука практически всегда происходит через си'шный интерфейс (либо быть си, либо через стыковку по правилам си) ---