[yt]
[vk]
- Пустой main и код возврата
- Запускаем c/c++ код из ассемблера: c, c++, asm
- Запускаем ассемблерный код из c/c++: c, c++, asm
- Указатели на функции: my_foreach.S, function_pointer.cpp
При завершении процесса судить о том, завершился он корректно или нет, можно по коду возврата. В код возврата выставляется в то значение, которое возвращается из main.
То есть значение rax
, которое вернет main
.
0 - процесс завершился корректно
любое другое число - была ошибка
Код возврата можно проверить следующим образом:
Запустить бинарь и сразу после этого сделать echo $?
. Запускать эту команду надо сразу после выполнения кода, так как туда сохраняется код возврата последнего запущенного процесса.
Также можно проверять следующим образом:
./a.out || echo "Failed"
В таком случае при ошибке будет выведен Failed.
Чтобы сделать return 0;
из ассемблера надо обнулить регистр rax
.
mov rax, 0 - работает, но лучше так не делать
xor rax, rax - лучше
xor eax, eax - еще лучше
Так как в C++
, в отличие от C
, разрешены перегруженные функции (различающиеся только аргументами), их имена изменяются с помощью name mangling — процесса преобразования имен функций. Подробнее можно прочитать здесь: https://www.emmtrix.com/wiki/Demystifying_C%2B%2B_-_Name_Mangling.
В нашем случае, функция multiply_add(int, int)
в скомпилированном виде будет иметь имя _Z12multiply_addii
. Линковщик для C++
-файла попытается найти именно эту функцию, которой в .S
файле нет, из-за чего произойдет ошибка линковки.
Использование extern "C"
заставляет компилятор генерировать имена функций по правилам C
(где имена должны быть уникальными, даже если аргументы разные). В результате линковщик будет искать функцию multiply_add
, которая уже присутствует в .S
файле, и линковка пройдет успешно.
Компиляция:
gcc main.S multiply_add.c
Компиляция:
g++ main.S multiply_add.c
Функция в cpp файлике должна быть объявлена как extern "C"
extern "C" int multiply_add(int a, int b)
c, asm Функция в .S файлике должна быть отмечена .global
.global multiply_add
В .c файлике должно быть объявление функции:
int multiply_add(int, int);
Компиляция:
gcc main.c multiply_add.S
Функция в .S файлике должна быть отмечена .global
.global multiply_add
В .cpp файлике должно быть объявление функции:
extern "C" int multiply_add(int, int);
Текст взят из материалов прошлых лекций
Декларатор вида
int (*pfunc)(int a, int b);
читается следующим образом: pfunc
- это указатель на функцию, принимающую два параметра типа int
и возвращающую значение типа int
.
То есть pfunc
- это переменная указатель на функцию. С помощью typedef
переменная может быть объявлена следующим образом:
typedef int (*func_t)(int a, int b); // func_t - тип указателя на функцию
func_t pfunc;
Любая переменная-указатель на функцию может принимать значение NULL
(или 0, или nullptr в Си++).
Если переменная-указатель на функцию не равна NULL, она должна указывать на функцию,
совместимую по передаваемым параметрам и возвращаемому значению.
Операция взятия адреса &
, примененная к имени функции, дает значение типа указателя на функцию
с соответствующим количеством и типом параметров и типом возвращаемого значения, например,
int handler(int value, int mask);
&handler // даст значение типа int (*)(int, int)
Однако, для имен функций автоматически выполняется неявное преобразование имени функции в указатель на функцию. То есть,
pfunc = handler;
это то же самое, что
pfunc = &handler;
Поэтому, как правило, явную операцию взятия адреса перед именами функций не пишут.