2012-06-07 15 views
12

Pozwoliłem gcc skompilować następujący przykład stosując -Wall -pedantic:Jak wydrukować adres funkcji?

#include <stdio.h> 

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

    return 0; 
} 

uzyskać:

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 

Linia 5 podjąłem zmienić kod tak jak w wierszu 6.

Czego mi brakuje do usunąć ostrzeżenie podczas drukowania adresu funkcji?

+1

Nie wiem, czy to jest dostępne, ale można zbadać za pomocą register_printf_function do [zdefiniować własny znak specjalny konwerter formatu i] (http://www.gnu.org/software/libc/manual/html_node/ Dostosowywanie-Printf.html # Dostosowywanie-Printf). –

+0

Problem nie polega na tym, że jest "niebezpieczny". Problem polega na tym, że konwersja nie jest definiowana przez język C, a zatem nie może być używana w zgodnym z C-kodzie. Można rzutować pośredni typ liczb całkowitych (wyniki definiowane przez implementację), o ile wiesz, że istnieje taki, który może pomieścić wskaźniki funkcji i obiektu). –

+0

@BobJarvis Czy masz pojęcie jak ominąć 'gcc' narzekając na nowo wprowadzony typ znaku konwersji (' warning: nieznany typ konwersji 'P' w formacie') podczas kompilacji z opcją '-Wall'? Ale to także inna historia ... – alk

Odpowiedz

12

Jest to w zasadzie jedyny przenośny sposób drukowania wskaźnika funkcji.

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'); 
+0

Podoba mi się to, chociaż milcząco ignoruje wszelkie problemy z endiansem ... ;-) – alk

+0

Nie pomyślałem o tym. +1. –

+1

Próbując uogólnić to podejście, zastanawiam się, czy wskaźniki funkcji różnych typów mają zagwarantowany ten sam rozmiar, chociaż odpowiedź (http://stackoverflow.com/a/189126/694576) na pytanie zadane przez larsmana sprawia, że ​​wątpię to ... - tak czy inaczej jest to inna historia. – alk

4

Cały ten pomysł jest rzeczywiście nieprzenośny, ponieważ niektóre systemy wykorzystują wskaźniki o różnych rozmiarach do kodu i danych.

To, czego naprawdę potrzebujesz, to specyficzna dla platformy wiedza o tym, jak duży jest wskaźnik funkcji, oraz o rzucie na integralny typ tego rozmiaru. Niestety, nie sądzę, aby ktokolwiek miał standaryzację intfuncptr_t analagiczną do intptr_t, która może pomieścić dowolny wskaźnik danych.


Jak zauważa R. in his answer, zawsze można traktować jako wskaźnik tablicy (ewentualnie signed lub unsigned) char, w ten sposób, że nie wymaga żadnych integralną typ prawidłowej wielkości.

+0

Nie ma żadnego integralnego typu danych; tylko 'unsigned char' (lub prawdopodobnie' char' tho that's messy and iffy). Każdy inny typ jest naruszeniem aliasingu, a nowoczesne kompilatory prawdopodobnie wyślą zły kod, który nie robi tego, co chciałeś. –

+0

@R ..: Konwersja nie może być naruszeniem aliasingu. Tylko jeśli masz dwie wartości l różnego typu odnoszące się do tej samej lokalizacji, może wystąpić naruszenie aliasingu. Zauważ, że mówię o 'reinterpret_cast (fnptr)', a nie '* reinterpret_cast (& fnptr)'. Twoja odpowiedź wiąże się z aliasingiem prawniczym, a moja nie ma wcale aliasów. Och, teraz widzę, mówisz o moim wyjaśnieniu twojej metody. Tak, "char" jest tam potrzebny, zaktualizuję. –

+1

Istnieje '(uintmax_t) & main', być może z' static_assert (sizeof (uintmax_t)> = sizeof (& main)) ;, aby, miejmy nadzieję, zapewnić brak niezdiagnozowanego UB z powodu konwersji poza zakresem –

4

Jest to w tym ostrzeżeniu: ISO C zabrania przekształcania wskaźnika funkcji na typ wskaźnika obiektu, który obejmuje void*. Zobacz także this question.

Po prostu nie można wydrukować adresu funkcji w sposób przenośny, więc nie można pozbyć się ostrzeżenia.

Możesz wydrukować wskaźnik funkcji za pomocą sugestii @R ..

+2

Według POSIX, reprezentacja wszystkie wskaźniki są takie same, więc w systemie POSIX można "memcpy" od zmiennej wskaźnika funkcji do zmiennej wskaźnika danych, a następnie ją wydrukować. I oczywiście można * zawsze * drukować reprezentację dowolnego typu bajt po bajcie, bez zależności od POSIX. –

4

Podczas konwertowania wskaźnika funkcji na wskaźnik pustej przestrzeni jest technicznie niebezpieczny, konwersja wskaźników funkcyjnych na wskaźniki void jest używana w standardzie POSIX, więc prawie na pewno działa na większości kompilatorów.

Sprawdź numer dlsym().

+3

Większość kodu C działa poza środowiskiem zgodnym z POSIX. –

+1

Przypisanie wartości zwracanej 'dlsym()' do prawidłowo zadeklarowanej zmiennej wskaźnika funkcji prowadzi do podobnego problemu, ale na odwrót. – alk

+1

Tak więc, zasadniczo, POSIX-2001 jest niezgodny z definicjami ABI, które używają innego rozmiaru dla wskaźników funkcji niż dla 'void *'. Dobrze wiedzieć. – cmaster