Страницы

Поиск по вопросам

среда, 5 июня 2019 г.

Объясните все возможные причины использования заголовков в мультифайловых проектах С

Посмотрите в начало стр. 5 здесь и объясните популярно, зачем в принципе нужен заголовок calc.h, если проект спокойно компилируется и запускается без заголовков вообще через: Andrey@AsusP5B ~/2 $ gcc driver.c calc.c -o prog
Andrey@AsusP5B ~/2 $ ./prog
Square of 5 is 25
Andrey@AsusP5B ~/2 $ Это что, новым компиляторам уже на заголовки наплевать или на прототипы функций, или это чистота написания кода, или просто лучше заголовки лучше сразу создавать, чтобы не было путаницы или х.з., так как в будущем они понадобятся для других целей... В теории, ПОЧЕМУ?


Ответ

Все работает, потому что си компилятор полагается на то, что Вы самостоятельно угадали сигнатуру. Возьмите driver.c, закомментируйте #include "calc.h" и "сделайте ошибку в вызове функции, где-то так: printf("
Square of %d is %d
", x, square()); Код скомпилируется и даже никто не ругнется. Но вот только работать он будет странно (как именно странно - зависит от ситуации, может упасть, а может выводить странные результаты). У меня, к примеру, выводит просто "Square of 5 is 1". Правильные заголовочные файлы позволяют компилятору знать правильную сигнатуру функции и находить подобные ошибки. Но почему же оно все-таки компилируется? Да все просто. Когда компилятор видит объявление функции, он генерирует для нее код и запоминает адрес начала в специальной таблице в виде имя-адрес (да, там нет параметров!). Когда нужно вставить вызов функции, просто смотрит в таблицу и поставляет "call адрес". Сами параметры перед этим напихает в стек push'ем. Но тут есть ещё одна хитрость. На самом деле вначале туда проставляются не адреса. Адреса уже проставляет линковщик. Именно по этой причине порядок компиляции файлов не столь важен. Почему же оно не работает, хотя и компилируется? Вызывающая сторона в стек параметры добавляет, но их фактическое кол-во нигде не отмечает. Вызываемая сторона просто достает с стека их. Но опять же, она не может никак узнать, что там именно то, что положила вызывающая сторона. Поэтому, что достали с стека, то и обработали. В с++ этот трюк уже не проходит. Там компилятор делает "манглироване имен". То есть в таблицу функций вставляет имя функции с хитрым описанием типов параметров. Это нужно, так как в С++ есть возможность создать несколько функций с одним именем и разным кол-вом/типом параметров.

Комментариев нет:

Отправить комментарий