2010-04-30 3 views
37

Есть ли способ напечатать указатель на функцию в ANSI C? Конечно, это означает, что вам нужно наложить указатель на указатель на void, но кажется, что это невозможно?Как форматировать указатель на функцию?

#include <stdio.h> 

int main() { 
    int (*funcptr)() = main; 

    printf("%p\n", (void*)funcptr); 
    printf("%p\n", (void*)main); 

    return 0; 
} 

$ GCC -ansi -pedantic -Wall test.c -o тест
test.c: В функции 'главной':
test.c: 6: предупреждение: ISO C запрещает преобразование указатель функции объект типа указателя
test.c: 7: предупреждения: ISO C запрещает преобразование указателя функции объекта указателю типа
$ ./test
0x400518
0x400518

Это «работает», но нестандартное ...

+0

Ну, я собирался принять ответ, который работал, пока он не был удален (хотя это не имеет смысла). –

+0

Можете ли вы преобразовать его в int (или 64-битный int в больших системах) и вместо этого напечатать? –

+0

@Michael Dorgan: Приведение указателя к целочисленному типу, отличному от 'intptr_t' или' uintptr_t', определяется реализацией, однако реализация этих типов является необязательной в соответствии со стандартом. – dreamlax

ответ

39

Единственный законный способ сделать это, чтобы получить доступ байтов, составляющих указатель, используя тип символов. Как это:

#include <stdio.h> 

int main() { 
    int (*funcptr)() = main; 
    unsigned char *p = (unsigned char *)&funcptr; 
    size_t i; 

    for (i = 0; i < sizeof funcptr; i++) 
    { 
     printf("%02x ", p[i]); 
    } 
    putchar('\n'); 

    return 0; 
} 

каламбурная функция указателя как void * или любой не символьного типа, а dreamlax's answer does, не определено поведение.

Что такое байты, составляющие указатель функции на самом деле Значение зависит от реализации. Например, они могут просто представлять индекс в таблицу функций.

+1

Не лучше ли использовать 'uintptr_t' вместо' unsigned char'? Как и в 'int (* funcptr)() = main; uintptr_t * p = (uintptr_t *) & funcptr; printf ("0x%" PRIxPTR "\ n", * p); ', избегая цикла? (Предполагается, что заголовки C99 'stdint.h' и' inttypes.h' поддерживаются.) –

+8

@Chris: 'uintptr_t' не имеет никакого отношения к указателям на функции и не является обязательным. Поэтому из POV написано что-то совершенно портативное нет, это не лучше. –

+1

ответ кафе правильный. Это мило, чтобы увидеть неопределенный код dreamlax, принятый в качестве ответа, в то время как правильный код томится здесь. –

1

С GCC 4.3.4, с помощью переключателей -O2 -Wstrict ступенчатости, ответ dreamlax будет производить:

warning: dereferencing type-punned pointer will break strict-aliasing rules 

Добавлено: Я думаю, что возражения против ответа CAF в о байтов и размера являются разумными, решение которых не рассматривается (каламбур не предназначен). Предложение Дастина о применении объединенного актера, вероятно, является законным (хотя из того, что я прочитал, похоже, есть некоторые дебаты, но ваш компилятор делает, что важно, чем закон). Но его код может быть упрощен (или затемненным, в зависимости от вашего вкуса) по однострочнику: (?, Но это «правильный»)

printf("%p\n", ((union {int (*from)(void); void *to;})funcptr).to); 

Это снимает ССАГПЗ предупреждения строгого сглаживания.

Совокупные отбрасывания не будут работать, если вы используете -специальный переключатель или используете, например, SGI IRIX, так что вам нужно использовать:

printf("%p\n", ((union {int (*from)(void); void *to;} *)&funcptr)->to); 

Но относительно оригинальный вопрос: его происхождение лежит в использовании -pedantic, который я думаю, немного педантичный :).

Дальнейшее редактирование: Примечание Вы не можете использовать основной в последнем примере, как и в:

printf("%p\n", ((union {int (*from)(void); void *to;}) main).to); // ok 
printf("%p\n", ((union {int (*from)(void); void *to;} *)&main)->to); // wrong! 

потому что, конечно & главный распадается на основной.

+2

Полезная информация, но это на самом деле не отвечает на его вопрос, это должен был быть комментарий к моему ответу, а не отдельный ответ. – dreamlax

+0

@dreamlax: Извините за отдельный ответ, но у меня всего 31 точка стека, меньше 50, необходимых для комментариев. Но я отвечу на комментарий как можно скорее. –

+0

@caf: Извините. Я вижу, что вы уже говорили об ответе Dreamlax в своем ответе. –

5

Там же использование союзов, которые могут обойти предупреждения/ошибки, но результат все равно (скорее всего) неопределенное поведение:

#include <stdio.h> 

int 
main (void) 
{ 
    union 
    { 
    int (*funcptr) (void); 
    void *objptr; 
    } u; 
    u.funcptr = main; 

    printf ("%p\n", u.objptr); 

    return 0; 
} 

Вы можете сравнить два указателей на функции (например, printf ("%i\n", (main == funcptr));) используя, если чтобы проверить, являются ли они равными или нет (я знаю, что полностью побеждает цель и может быть очень неуместным), но поскольку фактический вывод адреса указателя функции, то, что происходит, зависит от поставщика вашей целевой платформы C и ваш компилятор.

+0

Тип-punning с союзами не * неопределенный * поведение. Это поведение, определяемое реализацией, и на большинстве компиляторов делает именно то, что вы ожидаете. – Rufflewind

2

Включить указатель функции на целое число, а затем снова направить его на указатель, чтобы использовать «% p».

#include <stdio.h> 

int main() { 
    int (*funcptr)() = main; 

    printf("%p\n", (void *)(size_t) funcptr); 
    printf("%p\n", (void *)(size_t) main); 

    return 0; 
} 

Обратите внимание, что на некоторых платформах (например, 16-битный DOS, в «средний» или «компактных» моделях памяти), указатели на данные и указатели на функцию не являются таким же размером.

+0

Да, «16-разрядная DOS» работает по сегментированной архитектуре памяти. Например, 80286 будет иметь 4-байтовый 'void *' и 2-байтный 'size_t'. –

1

Попробуйте это:

#include <stdio.h> 
#include <inttypes.h> 


int main() { 
    int (*funcptr)() = main; 
    unsigned char *p = (unsigned char *)&funcptr; 
    int i; 

    /* sample output: 00000000004005e0 */ 
    printf("%016"PRIxPTR"\n", (uintptr_t)main); 
    /* sample output: 00000000004005e0 */ 
    printf("%016"PRIxPTR"\n", (uintptr_t)funcptr); 

    /* reflects the fact that this program is running on little-endian machine 
    sample output: e0 05 40 00 00 00 00 00 */ 
    for (i = 0; i < sizeof funcptr; i++) 
    { 
     printf("%02x ", p[i]); 
    } 
    putchar('\n'); 

    return 0; 
} 

использовал этот флаги:

gcc -ansi -pedantic -Wall -O2 -Wstrict-aliasing c.c 

Нет предупреждений, испускаемый с использованием этих флагов

+0

На машине, где требуется больше байтов для хранения указателя на функцию, чем указатель на UINT, ваши первые два printf будут выводить усеченную информацию. Чтобы gcc предупредил вас о приведениях, которые могут быть усечены (хотя, может быть, и не на вашем компьютере), возможно, вам нужен флаг, чтобы предупреждать о возможных усечениях, а не о псевдонимах. –

+0

Я думаю, что это самый строгий флаг, который может быть передан компилятору C. Завтра я загружу 32-битный Ubuntu и проверю, будет ли работать эта же кодовая база. Я буду тестировать также в Visual C++ 2008, если он будет работать так же –

+0

«На машине, где требуется больше байтов для хранения указателя на функцию, чем указатель на UINT» <- hmm .. (без сегментированной памяти pre-386, где у компилятора есть три варианта указателей: рядом, далекий, огромный (огромный - это скорее больше магии компилятора, чем реальная архитектура машины)), я помню на языке ассемблера, указатель переменной не существует, независимо от того, где он указывает на (UINT, char, short, function и т. д.). –

0

Я не уверен, если это кошерно, но он достигает эффект без петли, соединения, отливки к типам без указателей или дополнительные зависимости, кроме string.h

int (*funcptr)() = main; 
void* p = NULL; 
memcpy(&p, (void**) &funcptr, sizeof(funcptr)); 
printf("%p", p); 

Никаких предупреждений с gcc -ansi -pedantic -Wall -Wstrict-aliasing на GCC 7.1.1

Смежные вопросы