Почему адрес функции-члена так далек от бесплатных функций?

Возьмем этот пример: https://godbolt.org/z/gHqCSA

#include<iostream>

template<typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return(*p)(Args...) ) {
    return os << (void*)p;
}

template <typename ClassType, typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return (ClassType::*p)(Args...) )
{
    unsigned char* internal_representation = reinterpret_cast<unsigned char*>(&p);
    os << "0x" << std::hex;

    for(int i = 0; i < sizeof p; i++) {
        os << (int)internal_representation[i];
    }

    return os;
}
struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}

int main() {
    std::cout << "0. " << &test_debugger::var << std::endl;
    std::cout << "1. " << fun_void_void << std::endl;
    std::cout << "2. " << fun_void_double << std::endl;
    std::cout << "3. " << fun_double_double << std::endl;
}

// Prints:
//    0. 0x7018400100000000000
//    1. 0x100401080
//    2. 0x100401087
//    3. 0x100401093

Я вижу адрес функции-члена 0x7018400100000000000, что понятно, потому что указатели функций-членов имеют 16 байтов, а свободная функция как 0x100401080 имеет только 8 байтов.

Однако почему адрес функции-члена 0x7018400100000000000 так далеко от адреса свободной функции 0x100401080? то есть |0x7018400100000000000 - 0x100401080| = 0x70184000FFEFFBFEF80?

Почему не ближе, т.е. что-то вроде 0x100401... вместо 0x701840...? Или я неправильно печатаю адрес функции-члена?


person user    schedule 17.01.2020    source источник
comment
ваша ошибка заключается в том, что вы думаете, что адреса функций-членов имеют какое-либо отношение к адресам памяти   -  person M.M    schedule 17.01.2020
comment
печать btye (4) как int будет "4" вместо "04", необходимой для понимания памяти как шестнадцатеричной, используйте также setw и setfill   -  person kmdreko    schedule 17.01.2020
comment
Функция-указатель-член не является указателем. Когда вы приводите его к int, вы получаете все, что там есть, и, сказав компилятору притвориться, что там есть int, вы получаете то, о чем просили: бессмысленное значение.   -  person Pete Becker    schedule 17.01.2020


Ответы (2)


Ваша архитектура с прямым порядком байтов. Младший байт адреса находится в первом байте p, поэтому ваш адрес выводится задом наперед.

person 1201ProgramAlarm    schedule 17.01.2020
comment
И, как указал @kmdreko в комментарии к вопросу, без начальных нулей в любом байте в диапазоне от 0x00 до 0x0f. Переверните вывод и добавьте несколько нулей в очевидных местах, и адрес функции-члена окажется по соседству с остальными. - person ShadowRanger; 17.01.2020
comment
Может быть. Но указатель на функцию-член не является указателем, поэтому нет смысла притворяться, что это так. - person Pete Becker; 17.01.2020

Исправлен код, который автоматически определяет прямой/обратный порядок байтов: https://godbolt.org/z/XSvT5R

#include <iostream>
#include <iomanip>
#include <sstream>

inline bool is_big_endian() {
    long int longvalue = 1;

    // https://stackoverflow.com/questions/8978935/detecting-endianness
    unsigned char* representation = reinterpret_cast<unsigned char*>(&longvalue);
    return ( (unsigned) representation[sizeof(long int) - 1] ) == 1;
}

template<typename Pointer>
std::ostream& print_pointer(std::ostream& os, const Pointer& pointer) {
    const unsigned char* representation = (unsigned char*) &pointer;

    int precision = 0;
    bool haszeros = false;

    unsigned firsthexdigit;
    unsigned secondhexdigit;

    std::ostringstream stream;
    stream.flags( os.flags() );
    stream << std::hex;
    #define print_pointer_HEX_DIGIT \
        firsthexdigit = (unsigned) representation[index] >> 4 & 0xf; \
        secondhexdigit = (unsigned) representation[index] & 0xf; \
        if( haszeros || firsthexdigit ) { \
            precision++; \
            haszeros = true ; \
            stream << firsthexdigit; \
        } \
        if( haszeros || secondhexdigit ) { \
            precision++; \
            haszeros = true ; \
            stream << secondhexdigit; \
        }

    if( is_big_endian() ) {
        for(int index = 0; index < static_cast<int>(sizeof pointer); index++) {
            print_pointer_HEX_DIGIT
        }
    }
    else {
        for(int index = static_cast<int>(sizeof pointer - 1); index >= 0 ; index--) {
            print_pointer_HEX_DIGIT
        }
    }

    if( os.precision() - ++precision > 0 ) {
        return os << "0x" + std::string( os.precision() - ++precision, '0' ) + stream.str();
    }
    return os << "0x" + stream.str();
}

template<typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return(*pointer)(Args...) ) {
    return print_pointer(os , pointer);
}

template <typename ClassType, typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return (ClassType::*pointer)(Args...) ) {
    return print_pointer(os , pointer);
}

struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}

int main() {
    std::cout << "0. " << &test_debugger::var << std::endl;
    std::cout << "1. " << fun_void_void << std::endl;
    std::cout << "2. " << fun_void_double << std::endl;
    std::cout << "3. " << fun_double_double << std::endl;
    std::cout << "4. " << std::setfill('0') << std::setw(16) << fun_void_void << std::endl;
    std::cout << "5. " << std::setprecision(16) << fun_void_double << std::endl;
}
// Prints:
//    0. 0x100402e80
//    1. 0x100401118
//    2. 0x10040111f
//    3. 0x10040112b
//    4. 000000x100401118
//    5. 0x0000010040111f
person user    schedule 21.01.2020