Подтвердить что ты не робот

Как отформатировать указатель на функцию?

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

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

4b9b3361

Ответ 1

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

#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 * или любого несимвольного типа, как answerlax answer, является undefined.

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

Ответ 2

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

#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 библиотеки и вашего компилятора.

Ответ 3

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

Ответ 4

С gcc 4.3.4, используя переключатели -O2 -Wlict-aliasing, ответ dreamlax будет производить:

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

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

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

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

Агрегатные отсылки не будут работать, если вы используете -pedantic switch или используете, например, 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.

Ответ 5

Попробуйте следующее:

#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

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

Ответ 6

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