Как отформатировать указатель на функцию? - PullRequest
44 голосов

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

#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
test.c: в работе 'main':
test.c: 6: предупреждение: ISO C запрещает преобразование указателя функции на указатель объекта типа
test.c: 7: предупреждение: ISO C запрещает преобразование указатель на указатель на объект введите
$ ./test
0x400518
0x400518

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

Ответы [ 6 ]

44 голосов
/ 30 апреля 2010

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

#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 , является неопределенным поведением.

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

5 голосов
/ 30 апреля 2010

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

#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));), используя оператор if, чтобы проверить, равны они или нет (я знаю, что это полностью противоречит цели и вполне может быть неуместным), но насколько это реально выводится адрес указателя на функцию, что происходит, зависит от поставщика библиотеки C вашей целевой платформы и вашего компилятора.

2 голосов
/ 30 апреля 2010

Приведите указатель функции к целому числу, затем снова приведите его к указателю, чтобы использовать «% 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 в «средних» или «компактных» моделях памяти) указатели на данные и указатели на функции имеют разный размер.

1 голос
/ 30 апреля 2010

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

#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

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

1 голос
/ 30 апреля 2010

При использовании gcc 4.3.4 с использованием ключей -O2 -Wstrict-aliasing ответ dreamlax выдаст:

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

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

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

Это удаляет предупреждение gcc о строгом псевдониме (но верно ли это?).

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

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

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

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

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

потому что, конечно, & main распадается на main .

0 голосов
/ 21 ноября 2017

Я не уверен, является ли это кошерным, но он достигает эффекта без цикла, объединений, приведений к типам без указателей или дополнительных зависимостей помимо 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...