Как распечатать адрес функции?

Я позволил gcc скомпилировать следующий пример, используя -Wall -pedantic:

#include <stdio.h>

int main(void)
{
  printf("main: %p\n", main); /* line 5 */
  printf("main: %p\n", (void*) main); /* line 6 */

  return 0;
}

Я получил:

main.c:5: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘int (*)()’
main.c:6: warning: ISO C forbids conversion of function pointer to object pointer type

Строка 5 заставила меня изменить код, как в строке 6.

Что мне не хватает, чтобы удалить предупреждение при печати адреса функции?


person alk    schedule 07.06.2012    source источник
comment
Не уверен, что это доступно вам, но вы можете изучить использование register_printf_function для определите свой собственный символ специального формата и конвертер.   -  person Bob Jarvis - Reinstate Monica    schedule 07.06.2012
comment
Проблема не в том, что это опасно. Проблема в том, что преобразование не определяется языком C и, следовательно, не может использоваться в соответствии с кодом C. Вы можете использовать промежуточный целочисленный тип (результаты, определяемые реализацией), если вы знаете, что существует такой тип, который может содержать указатели как на функции, так и на объекты).   -  person R.. GitHub STOP HELPING ICE    schedule 07.06.2012
comment
@BobJarvis. Вы знаете, как обойтись gcc с жалобами на недавно введенный символ типа преобразования (warning: unknown conversion type character ‘P’ in format) при компиляции с опцией -Wall? Но это тоже другая история ...   -  person alk    schedule 07.06.2012
comment
@R ..: Это C. Мы вообще опасны.   -  person Bob Jarvis - Reinstate Monica    schedule 07.06.2012
comment
@alk: попробуйте добавить параметр -Wno-format после -Wall, который (если память не изменяет) отключит проверку формата printf / scanf.   -  person Bob Jarvis - Reinstate Monica    schedule 07.06.2012
comment
@BobJarvis Если бы я мог сделать то, что вы предложили, мне бы не пришлось задавать этот оригинальный вопрос ... | -}. В любом случае я нашел это здесь: gcc.gnu.org/bugzilla/show_bug.cgi? id = 47781 говорит мне, что то, что я хотел бы, не существует (пока?).   -  person alk    schedule 07.06.2012


Ответы (4)


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

size_t i;
int (*ptr_to_main)() = main;
for (i=0; i<sizeof ptr_to_main; i++)
    printf("%.2x", ((unsigned char *)&ptr_to_main)[i]);
putchar('\n');
person R.. GitHub STOP HELPING ICE    schedule 07.06.2012
comment
Мне это нравится, хотя он молча игнорирует любые проблемы с порядком байтов ... ;-) - person alk; 07.06.2012
comment
Пытаясь обобщить этот подход, мне интересно, гарантированно ли указатели функций разных типов имеют одинаковый размер, хотя ответ (stackoverflow.com/ a / 189126/694576) на вопрос, на который ссылается larsman, заставляет меня усомниться в этом ... - впрочем, это уже отдельная история. - person alk; 07.06.2012
comment
Преобразование между любыми двумя указателями на функции определено и сохраняет значения в оба конца в соответствии со стандартом C; см. конец 6.3.2.3. - person R.. GitHub STOP HELPING ICE; 07.06.2012
comment
Я бы использовал один раз, вне цикла, но это лучший подход. - person Ben Voigt; 07.06.2012
comment
Спасибо за редактирование. Первоначально я неправильно прочитал вопрос OP как использующий адрес printf и забыл исправить одну ссылку. :-) - person R.. GitHub STOP HELPING ICE; 07.06.2012
comment
@R .. Я что-то пропустил, но как это переносится? Вы по-прежнему преобразуете указатель функции (&ptr_to_main) в тип указателя на объект (unsigned char *), но C не допускает преобразований между типами указателей на функции и типами указателей объектов. - person ouah; 24.12.2014
comment
@ouah: Действительно, вы что-то упустили. ptr_to_main - объект, а &ptr_to_main - указатель на объект. - person Ben Voigt; 24.12.2014
comment
@BenVoigt Спасибо, да, согласен, я, должно быть, устал :) - person ouah; 24.12.2014
comment
Это предполагает CHAR_BIT==8. С CHAR_BIT > 8 он по-прежнему будет печатать полное значение каждого байта, но вывод может быть неоднозначным; 12345 может быть 12:345 или 123:45. (Это практически никогда не проблема.) - person Keith Thompson; 17.02.2015
comment
@Keith Thompson Может использовать printf("%.*x", (CHAR_BIT+3)/4, ((unsigned char *)&ptr_to_main)[i]); Это позволяет избежать магического числа 2. - person chux - Reinstate Monica; 19.07.2016

Это прямо здесь, в предупреждении: ISO C запрещает преобразование указателя функции в тип указателя объекта, который включает void*. См. Также этот вопрос.

Вы просто не можете распечатать адрес функции переносимым способом, поэтому вы не можете избавиться от предупреждения.

Вы можете распечатать указатель функции, используя предложение @R ...

person Fred Foo    schedule 07.06.2012
comment
Согласно POSIX, представление всех указателей одинаково, поэтому в системе POSIX вы можете memcpy из переменной указателя функции в переменную указателя данных, а затем распечатать это. И, конечно, вы можете всегда распечатать представление любого типа побайтно, без зависимости от POSIX. - person R.. GitHub STOP HELPING ICE; 07.06.2012

Вся эта идея действительно непереносима, поскольку некоторые системы используют указатели разного размера для кода и данных.

Что вам действительно нужно, так это знание платформы о том, насколько велик указатель на функцию, и приведение к целочисленному типу такого размера. К сожалению, я не думаю, что кто-то стандартизировал intfuncptr_t аналог intptr_t, который может содержать любой указатель данных.


Как отмечает Р. в своем ответе, вы всегда можете рассматривать указатель как массив (возможно signed или unsigned) char, таким образом, вам не понадобится какой-либо цельный тип правильного размера.

person Ben Voigt    schedule 07.06.2012
comment
Не какой-либо интегральный тип данных; только unsigned char (или, вероятно, char, хотя это беспорядочно и ненадежно). Любой другой тип является нарушением псевдонима, и современные компиляторы могут выдавать плохой код, который не выполняет то, что вы хотели. - person R.. GitHub STOP HELPING ICE; 07.06.2012
comment
@R ..: преобразование не может быть нарушением псевдонима. Только если у вас есть два lvalue разного типа, относящиеся к одному и тому же местоположению, может быть нарушение псевдонима. Обратите внимание, что я говорю о reinterpret_cast<integral_type>(fnptr), а не о *reinterpret_cast<integral_type*>(&fnptr). Ваш ответ касается легального псевдонима, мой - никакого. О, теперь я понимаю, вы говорите о моем объяснении вашего метода. Да там char нужен, буду обновлять. - person Ben Voigt; 07.06.2012
comment
Есть (uintmax_t)&main, возможно, с static_assert( sizeof(uintmax_t) >= sizeof(&main) );, чтобы, надеюсь, гарантировать отсутствие недиагностированного UB из-за преобразования вне диапазона - person M.M; 24.12.2014

Хотя преобразование указателя функции в указатель void технически опасно, преобразование указателей функций в указатели void используется в стандарте POSIX, поэтому почти наверняка будет работать на большинстве компиляторов.

Посмотрите dlsym().

person Lalaland    schedule 07.06.2012
comment
Большая часть кода C выполняется вне POSIX-совместимой среды. - person Ben Voigt; 07.06.2012
comment
Присвоение возвращаемого значения dlsym() правильно объявленной переменной-указателю функции приводит к аналогичной проблеме, но наоборот. - person alk; 07.06.2012
comment
Таким образом, POSIX-2001 несовместим с определениями ABI, которые используют другой размер для указателей функций, чем для void*. Приятно знать. - person cmaster - reinstate monica; 24.12.2014