Вот что g cc и Clang производят для вашего кода в его нынешнем виде:
main: # @main
xor eax, eax
ret
код на Godbolt
Так что в этом случае, у нас нет ни раннего, ни позднего связывания. Скорее, у нас вообще нет привязки к функции - вы не использовали результат, полученный при вызове функции (напрямую или через указатель), поэтому компилятор просто не генерировал никакого кода для вызова функции в все.
Мы можем исправить это с помощью кода в следующем порядке:
#include <iostream>
int add (int x, int y)
{
return x+y;
}
int main()
{
int a=add(5,6);//early binding
int (*p_add)(int,int)=add;
int b=p_add(5,19);
std::cout << b;
}
В этом случае компилятор все еще обнаруживает, что результат функции не зависит ни от чего во время компиляции , поэтому он вычисляет значение во время компиляции и печатает его как константу:
mov esi, 24
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)ant:
Код на Godbolt
Итак, у нас все еще нет любая реальная «привязка» к функции. Давайте использовать входы, которые он не будет знать до времени выполнения:
#include <iostream>
#include <cstdlib>
int add (int x, int y)
{
return x+y;
}
int main()
{
int x1 = rand();
int x2 = rand();
int a=add(x1, x2);//early binding
int (*p_add)(int,int)=add;
int b=p_add(x1,x2);
std::cout << b;
}
Этот источник генерирует следующий объектный код:
call rand
mov ebx, eax
call rand
mov edi, OFFSET FLAT:_ZSt4cout
lea esi, [rbx+rax]
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
Компилятор все еще знает, что указатель согласованно указывает на одну конкретную функцию, поэтому, хотя исходный код показывает функцию, вызываемую через указатель, в объектном коде мы не вызываем функцию через указатель ... и на самом деле мы по-прежнему не вызываем функцию совсем. Вместо этого код для тела функции был сгенерирован встроенным.
Чтобы получить реальный вызов функции через указатель, у нас может быть указатель, который ссылается на любую из двух разных функций, и он не будет до времени выполнения будет ясно, какой из двух использовать в конкретном случае. Например:
#include <iostream>
#include <cstdlib>
int add (int x, int y)
{
return x+y;
}
int sub(int x, int y) {
return x-y;
}
int main()
{
int x1 = rand();
int x2 = rand();
int z = rand() % 2;
int (*p_add)(int,int) = z ? add : sub;
int b=p_add(x1,x2);
std::cout << b;
}
Это (наконец-то!) Делает вызов через указатель фактически происходящим как вызов через указатель:
call rand
mov edx, OFFSET FLAT:sub(int, int) ; start by assuming we'll subract
mov esi, r12d
mov edi, ebp
test al, 1 ; then see if we have an odd or even number
mov eax, OFFSET FLAT:add(int, int)
cmove rax, rdx ; if necessary, point to add
call rax ; and finally call the function via the pointer
mov edi, OFFSET FLAT:_ZSt4cout
mov esi, eax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
Код на Godbolt
Сводка
Если во время компиляции будет ясно, какая функция будет вызываться, компилятор, вероятно, не сгенерирует код для вызова функции через указатель, даже если это что показывает исходный код.