Czy gcc może wyświetlać kod C po wstępnym przetworzeniu?

105

Używam biblioteki open source, która wydaje się mieć wiele dyrektyw wstępnego przetwarzania do obsługi wielu języków innych niż C.Aby móc przestudiować, co robi biblioteka Chciałbym zobaczyć kod C, który kompiluję po wstępnym przetworzeniu , bardziej jak to, co bym napisał.

Czy gcc (lub inne narzędzie powszechnie dostępne w Linuksie) może odczytać tę bibliotekę, ale wyprowadzić kod C, który ma przetwarzanie wstępne przekonwertowane na cokolwiek i jest również czytelny dla człowieka?

LGTrader
źródło
Wstępnie przetworzony kod nie będzie już zawierał żadnych dyrektyw preprocesora, ale jestem prawie pewien, że będzie o wiele mniej czytelny niż przed przetwarzaniem wstępnym ...
Alex W
2
@AlexW - To zależy całkowicie od tego, jak strasznie ludzie piszący kod nadużyli preprocesora.
Fałszywe imię,
1
Rozważ zmianę zaakceptowanej odpowiedzi tutaj. gcc -Ejest bardziej przydatne niż konieczność przepisywania wiersza, aby działał cpp.
Gray

Odpowiedzi:

194

Tak. Przekaż gcc-E opcję . Spowoduje to wygenerowanie wstępnie przetworzonego kodu źródłowego.

mipadi
źródło
12
Jeśli twoje polecenia kompilatora mają już parametr, taki jak -o something.oty, możesz również chcieć go zmienić na -o something.i. W przeciwnym razie wstępnie przetworzone dane wyjściowe będą znajdować się w .opliku.
Tor Klingberg
@TorKlingberg Czy mogę to zrobić dla wielu plików naraz?
user2808264
@ user2808264gcc -E file1.c file2.c ...
Matthieu
68

cpp jest preprocesorem.

Uruchom, cpp filename.caby wyświetlić wstępnie przetworzony kod, lub lepiej, przekieruj go do pliku z rozszerzeniem cpp filename.c > filename.preprocessed.

tpdi
źródło
2
Myślę, że to najlepsza odpowiedź, ponieważ bezpośrednio demonstruje CPP. Systemy Linux (przynajmniej Manjaro) też wydają się mieć domyślnie -E. Tak czy inaczej otrzymuję te same wyniki z tego polecenia. diffpojawia się żadna różnica w plikach. Wygląda to również na przydatny sposób na wstępne przetwarzanie kodu w poszukiwaniu błędów w makrach. Świetne pytanie i świetna odpowiedź (IALCTHW).
lee8oi
17

Używam gcc jako preprocesora (dla plików html). Robi dokładnie to, co chcesz. Rozszerza dyrektywy "# -", a następnie wyświetla czytelny plik. (ŻADEN z innych preprocesorów C / HTML, których próbowałem, nie robił tego - łączą wiersze, dławią się znakami specjalnymi itp.) Zakładając, że masz zainstalowane gcc, wiersz poleceń to:

gcc -E -xc -P -C -traditional-cpp code_before.cpp> code_after.cpp

(Nie musi to być „cpp”). Doskonały opis tego zastosowania znajduje się pod adresem http://www.cs.tut.fi/~jkorpela/html/cpre.html .

„-Traditional-cpp” zachowuje białe znaki i tabulatory.

Jack Ritter
źródło
Wielkie dzięki, jest to bardzo pomocne przy generowaniu pythona cffi cdef!
amirouche
13

-save-temps

To kolejna dobra opcja, o której należy pamiętać:

gcc -save-temps -c -o main.o main.c

main.c

#define INC 1

int myfunc(int i) {
    return i + INC;
}

a teraz, oprócz normalnego wyjścia main.o, bieżący katalog roboczy zawiera również następujące pliki:

  • main.i jest żądanym wstępnie posiadanym plikiem zawierającym:

    # 1 "main.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 31 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 32 "<command-line>" 2
    # 1 "main.c"
    
    
    int myfunc(int i) {
        return i + 1;
    }
  • main.s jest bonusem :-) i zawiera wygenerowany zestaw:

        .file   "main.c"
        .text
        .globl  myfunc
        .type   myfunc, @function
    myfunc:
    .LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    $1, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE0:
        .size   myfunc, .-myfunc
        .ident  "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
        .section    .note.GNU-stack,"",@progbits

Jeśli chcesz to zrobić dla dużej liczby plików, rozważ użycie zamiast tego:

 -save-temps=obj

który zapisuje pliki pośrednie w tym samym katalogu, co dane -owyjściowe obiektu zamiast w bieżącym katalogu roboczym, unikając w ten sposób potencjalnych konfliktów basename.

Zaletą tej opcji -Ejest to, że można ją łatwo dodać do dowolnego skryptu kompilacji, nie ingerując zbytnio w samą kompilację.

Kolejną fajną rzeczą dotyczącą tej opcji jest dodanie -v:

gcc -save-temps -c -o main.o -v main.c

faktycznie pokazuje używane jawne pliki zamiast brzydkich tymczasowych /tmp, więc łatwo jest dokładnie wiedzieć, co się dzieje, co obejmuje etapy wstępnego przetwarzania / kompilacji / asemblacji:

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s

Przetestowano w Ubuntu 19.04 amd64, GCC 8.3.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
1
Znacznie bardziej eleganckie niż -E, ponieważ mogę po prostu dodać -save-temps do CFLAGS bez zmiany ogólnego zachowania skryptu budującego. Dziękuję Ci!
EvertW
Jest to rzeczywiście bardzo przydatne, a -E jest bardzo wygodne dla pojedynczych plików.
Subin Sebastian
9

Biegać:

gcc -E <file>.c

lub

g++ -E <file>.cpp
Andrii Pyvovar
źródło
1

Załóżmy, że mamy plik Message.cpp lub .c

Kroki 1: Przetwarzanie wstępne (argument -E)

g ++ -E. \ Message.cpp> P1

Wygenerowany plik P1 ma rozwinięte makra, a zawartość pliku nagłówkowego i komentarze są usuwane.

Krok 2: Przetłumacz wstępnie przetworzony plik na zestaw (argument -S). To zadanie jest wykonywane przez kompilator

g ++ -S. \ Message.cpp

Generowany jest asembler (ASM) (Message.s). Zawiera cały kod asemblera.

Krok 3: Przetłumacz kod asemblera na kod obiektowy. Uwaga: Message.s został wygenerowany w kroku 2. g ++ -c. \ Message.s

Generowany jest plik Object o nazwie Message.o. To jest forma binarna.

Krok 4: Łączenie pliku obiektowego. To zadanie jest wykonywane przez konsolidator

g ++. \ Message.o -o MessageApp

Tutaj generowany jest plik exe MessageApp.exe.

#include <iostream>
using namespace std;

 //This a sample program
  int main()
{
cout << "Hello" << endl;
 cout << PQR(P,K) ;
getchar();
return 0;
}
Pranav Kumar
źródło