Может ли gcc выводить код C после предварительной обработки?

105

Я использую библиотеку с открытым исходным кодом, которая, кажется, имеет множество директив предварительной обработки для поддержки многих языков, отличных от C. Чтобы я мог изучить, что делает библиотека, я хотел бы увидеть код C, который я компилирую после предварительной обработки , больше похоже на то, что я напишу.

Может ли gcc (или любой другой инструмент, обычно доступный в Linux) читать эту библиотеку, но выводить код C, в котором предварительная обработка преобразована во что угодно и который также может читать человек?

LGTrader
источник
В предварительно обработанном коде больше не будет никаких директив препроцессора, но я почти уверен, что он будет гораздо менее читабельным, чем до предварительной обработки ...
Alex W
2
@AlexW - Это полностью зависит от того, насколько ужасно люди, пишущие код, злоупотребляли препроцессором.
Fake Name
1
Пожалуйста, подумайте об изменении принятого ответа здесь. gcc -Eэто более полезно, чем необходимость переписывать строку, чтобы она работала cpp.
Gray

Ответы:

194

Да. Передайте gcc -Eопцию. Это выведет предварительно обработанный исходный код.

мипади
источник
12
Если в ваших командах компилятора уже есть такой параметр, -o something.oвы также можете изменить его на -o something.i. В противном случае предварительно обработанный вывод будет в .oфайле.
Тор Клингберг,
@TorKlingberg Могу ли я сделать это для нескольких файлов одновременно?
user2808264
@ user2808264gcc -E file1.c file2.c ...
Матье
68

cpp препроцессор.

Запустить cpp filename.cдля вывода предварительно обработанного кода или, лучше, перенаправить его в файл с расширением cpp filename.c > filename.preprocessed.

TPDI
источник
2
Я думаю, что это лучший ответ, потому что он напрямую демонстрирует cpp. Системы Linux (по крайней мере, Manjaro), похоже, тоже по умолчанию имеют -E. В любом случае я получаю те же результаты от этой команды. diffоказывается никакой разницы в файлах. Это также выглядит как полезный способ предварительной обработки кода для поиска ошибок в ваших макросах. Отличный вопрос и отличный ответ (IALCTHW).
lee8oi
17

Я использую gcc в качестве препроцессора (для файлов html). Он делает именно то, что вы хотите. Он расширяет директивы «# -», а затем выводит читаемый файл. (НИ ОДИН из других препроцессоров C / HTML, которые я пробовал, не делает этого - они объединяют строки, подавляются специальными символами и т. Д.) Если у вас установлен gcc, командная строка выглядит так:

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

(Не обязательно должно быть cpp.) На http://www.cs.tut.fi/~jkorpela/html/cpre.html есть отличное описание этого использования .

"-Traditional-cpp" сохраняет пробелы и табуляции.

Джек Риттер
источник
Большое спасибо, это очень полезно для создания python cffi cdef!
amirouche
13

-save-temps

Это еще один хороший вариант, о котором следует помнить:

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

main.c

#define INC 1

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

и теперь, помимо обычного вывода main.o, текущий рабочий каталог также содержит следующие файлы:

  • main.i желаемый предварительно настроенный файл, содержащий:

    # 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 это бонус :-) и содержит сгенерированную сборку:

        .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
    

Если вы хотите сделать это для большого количества файлов, рассмотрите возможность использования вместо этого:

 -save-temps=obj

который сохраняет промежуточные файлы в том же каталоге, что и -oвыходные данные объекта, а не в текущем рабочем каталоге, что позволяет избежать потенциальных конфликтов базовых имен.

Преимущество этого варианта в -Eтом, что его легко добавить в любой скрипт сборки, не влияя при этом на саму сборку.

Еще одна интересная особенность этой опции - если вы добавите -v:

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

он фактически показывает явно используемые файлы вместо уродливых временных файлов /tmp, поэтому легко узнать, что именно происходит, включая этапы предварительной обработки / компиляции / сборки:

/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

Протестировано в Ubuntu 19.04 amd64, GCC 8.3.0.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1
Намного более элегантно, чем -E, потому что я могу просто добавить -save-temps в CFLAGS, не меняя общего поведения скрипта сборки. Спасибо!
EvertW
Это действительно очень полезно, и -E очень удобно для отдельных файлов.
Субин Себастьян
9

Бегать:

gcc -E <file>.c

или

g++ -E <file>.cpp
Андрей Пивовар
источник
1

Предположим, у нас есть файл Message.cpp или файл .c

Шаги 1: предварительная обработка (аргумент -E)

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

Сгенерированный файл P1 имеет расширенные макросы и содержимое файла заголовка, а комментарии удалены.

Шаг 2: Преобразуйте предварительно обработанный файл в сборку (аргумент -S). Эту задачу выполняет компилятор

g ++ -S. \ Message.cpp

Создается ассемблер (ASM) (Message.s). В нем есть весь ассемблерный код.

Шаг 3. Переведите ассемблерный код в объектный код. Примечание. Message.s был создан на шаге 2. g ++ -c. \ Message.s

Создается объектный файл с именем Message.o. Это двоичная форма.

Шаг 4: Связывание объектного файла. Эту задачу выполняет компоновщик

g ++. \ Message.o -o MessageApp

Здесь создается exe-файл MessageApp.exe.

#include <iostream>
using namespace std;

 //This a sample program
  int main()
{
cout << "Hello" << endl;
 cout << PQR(P,K) ;
getchar();
return 0;
}
Пранав Кумар
источник