Получить адрес функции в виде числа в языке Си++ можно через приведение указателя на функцию к нужному числовому типу. Делается это через стандартную шаблонную функцию reinterpret_cast<...>(...). Данная функция не приводит типы, а позволяет рассматривать область памяти, которая выделена под переменную, в виде заданного в угловых стрелках типа. То есть, выглядит преобразование следующим образом:
#include <iostream>
#include <stdio.h>
// Код функции func() находится в ячейке, например, памяти 64C18F7249B0
int func()
{
return 7;
}
int main()
{
// C-стиль получения адреса в виде числа,
// такую запись можно использовать и в C++
unsigned long a = reinterpret_cast<unsigned long>( func );
printf("a = %lX\n", a);
// C++ - стиль вывода в консоль напрямую
std::cout << func;
// C++ - стиль вывода в консоль через приведение к типу void*
std::cout << reinterpret_cast<void*>(func);
return 0;
}
В консоль будет выведено:
a = 64C18F7249B0
1
0x64C18F7249B0
Получение адреса работает одинаково для Си и Си++. А вот вывод значения указателя на функцию в стандартный поток для Си++ имеет особенности. Если просто попыпаться напечатать значение указателя на функцию через стандартный консольный поток std::cout, то будет выведено 1. Почему так?
Дело в том, что переменная func имеет тип указателя на функцию, а не просто числовой тип. Вывод зависит от функции-оператора <<, которая имеет кучу прототипов для различных типов:
- basic_ostream& operator<<( short value );
- basic_ostream& operator<<( unsigned short value );
- basic_ostream& operator<<( int value );
- basic_ostream& operator<<( unsigned int value );
- basic_ostream& operator<<( long value );
- basic_ostream& operator<<( unsigned long value );
- basic_ostream& operator<<( long long value );
- basic_ostream& operator<<( unsigned long long value ); (since C++11)
- basic_ostream& operator<<( float value );
- basic_ostream& operator<<( double value );
- basic_ostream& operator<<( long double value );
- basic_ostream& operator<<( bool value );
- basic_ostream& operator<<( const void* value );
- basic_ostream& operator<<( const volatile void* value ); (since C++23)
- basic_ostream& operator<<( std::nullptr_t ); (since C++17)
- basic_ostream& operator<<( std::basic_streambuf<CharT, Traits>* sb );
- basic_ostream& operator<<( std::ios_base& (*func)(std::ios_base&) );
- basic_ostream& operator<<( std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );
- basic_ostream& operator<<( std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) );
Перед выводом язык видит, что передан указатель (неважно, на переменную или на функцию), и преобразует его в bool-значение. Почему так происходит? Перегрузки оператора << для вывода адреса функций нет. Нет потому, что тип указателя на функцию зависит от вида самой функции, а он для разных функций неопределен - ибо он может быть совершенно разный.
Зато есть перегрузка для вывода bool. А у указателя любого типа есть преобразование в тип bool, причем оно стоит на первом месте в приоритете по сравнению со всеми другими преобразованиями типов. Соответственно указатель на функцию неявно преобразуется в bool.
Вывести значение адреса можно явно преобразовав указатель на функцию к указателю на void* (перегрузка оператора << для которого как раз печатает значение адреса):
29.7.5.1 Class template basic_ostream [ostream]
namespace std {
template<class charT, class traits = char_traits<charT>>
class basic_ostream : virtual public basic_ios<charT, traits> {
public:
...
// 29.7.5.2, formatted output
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& (*pf)(basic_ostream<charT, traits>&));
basic_ostream<charT, traits>&
operator<<(basic_ios<charT, traits>& (*pf)(basic_ios<charT, traits>&));
basic_ostream<charT, traits>&
operator<<(ios_base& (*pf)(ios_base&));
basic_ostream<charT, traits>& operator<<(bool n);
basic_ostream<charT, traits>& operator<<(short n);
basic_ostream<charT, traits>& operator<<(unsigned short n);
basic_ostream<charT, traits>& operator<<(int n);
basic_ostream<charT, traits>& operator<<(unsigned int n);
basic_ostream<charT, traits>& operator<<(long n);
basic_ostream<charT, traits>& operator<<(unsigned long n);
basic_ostream<charT, traits>& operator<<(long long n);
basic_ostream<charT, traits>& operator<<(unsigned long long n);
basic_ostream<charT, traits>& operator<<(float f);
basic_ostream<charT, traits>& operator<<(double f);
basic_ostream<charT, traits>& operator<<(long double f);
То же самое происходит и при выводе в стандартный поток. Указатель преобразуется в bool и прогоняется через bool-вариант функции-оператора <<, и в стандартный поток попадает значение true, которое отображается как 1. В случае же приведения к типу void*, используется вариант с параметром const void* value, который выводит шестнадцатеричное число.