zoukankan      html  css  js  c++  java
  • C代码编译成可执行程序的过程

    C代码通过编译器编译成可执行代码,经历了四个阶段,依次为:预处理、编译、汇编、链接。

    接下来详细讲解各个阶段

    一、预处理

    1、任务:进行宏定义展开、头文件展开、条件编译,不检查语法。

    2、命令:gcc -E [源文件]  -o [预处理文件]

    3、案例:用gcc编译器预处理demo1.c代码,预处理后的文本放到demo1.i中。(gcc -E demo1.c -o demo1.i)

    demo1.c代码如下:

     1 #include <stdio.h>
     2 
     3 #define add(a, b) (a + b)
     4 #define sub(a, b) (a - b)
     5 
     6 int main(void)
     7 {
     8     int a, b, c, d;
     9 #ifndef __cplusplus
    10     a = b = c = d = 1;
    11 #else
    12     a = b = c = d = 2;
    13 #endif
    14     printf("num = %d
    ", sub(add(a, b), add(c, d)));
    15     return 0;
    16 }
    demo1.c

    生成的demo1.i代码如下:

     1 # 1 "demo.c"
     2 # 1 "<command-line>"
     3 # 1 "/usr/include/stdc-predef.h" 1 3 4
     4 # 1 "<command-line>" 2
     5 
     6 此处省略800行...
     7 
     8 extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
     9 # 943 "/usr/include/stdio.h" 3 4
    10 
    11 # 2 "demo.c" 2
    12 
    13 
    14 int main(void)
    15 {
    16     int a, b, c, d;
    17 
    18     a = b = c = d = 1;
    19 
    20 
    21 
    22     printf("num = %d
    ", ((a + b) - (c + d)));
    23     return 0;
    24 }
    demo1.i

    通过案例可以发现:#define宏定义、stdio.h头文件、#ifdef条件编译都被替换了,并且你还可以故意写一句有语法错误的代码,但是却不会报错。由于stdio.h头文件长达800多行,因此在demo1.i中只截取开头和结尾的几行。

        

    二、编译

    1、任务:检查语法,将预处理过的文件编译生成汇编文件。

    2、命令:gcc -S [源文件] -o [汇编文件]

    3、案例;用gcc编译器编译demo2.c代码,编译后的汇编代码放到demo2.s中。(gcc -S demo2.c  -o demo2.s)

    demo2.c代码如下:

    1 #include <stdio.h>
    2 
    3 int main(int argc, char *argv[])
    4 {
    5     printf("hello world
    ");
    6     return 0;
    7 }
    demo2.c

    生成的demo2.s代码如下:

     1     .file    "demo2.c"
     2     .section    .rodata
     3 .LC0:
     4     .string    "hello world"
     5     .text
     6     .globl    main
     7     .type    main, @function
     8 main:
     9 .LFB0:
    10     .cfi_startproc
    11     pushq    %rbp
    12     .cfi_def_cfa_offset 16
    13     .cfi_offset 6, -16
    14     movq    %rsp, %rbp
    15     .cfi_def_cfa_register 6
    16     subq    $16, %rsp
    17     movl    %edi, -4(%rbp)
    18     movq    %rsi, -16(%rbp)
    19     movl    $.LC0, %edi
    20     call    puts
    21     movl    $0, %eax
    22     leave
    23     .cfi_def_cfa 7, 8
    24     ret
    25     .cfi_endproc
    26 .LFE0:
    27     .size    main, .-main
    28     .ident    "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    29     .section    .note.GNU-stack,"",@progbits
    demo.s

    通过案例可以看出,我们写的c代码被编译成了汇编代码。故意写错一个语法点,编译器将报错。

    三、汇编

    1、任务:将汇编文件生成目标文件(2进制文件)。

    2、命令:gcc -s [源文件] -o [目标文件]

    3、案例:用gcc编译器汇编demo3.c代码,编译后的二进制代码放到demo3.o中。(gcc -c demo3.c  -o demo3.o)

    demo3.c代码如下:

    1 #include <stdio.h>
    2 
    3 int main(int argc, char *argv[])
    4 {
    5     printf("hello world
    ");
    6     return 0;
    7 }
    demo3.c

    生成的demo3.o代码如下:

    通过汇编阶段,文本代码变成了二进制代码,也就是计算机可以识别的代码。c语言中,二进制代码文件是以.o为后缀名的。

    四、链接

    1、任务:找到依赖的库文件,将目标文件链接为可执行程序。

    2、命令:gcc -c [目标文件] -o [可执行程序] -l[动态库名]

    3、案例:通过gcc编译器让demo4链接自己制作的libadd.so动态库,并把demo4编译成可执行程序。gcc demo4.c -o demo4 -L./ -ladd

    demo4.c代码如下:

    1 #include <stdio.h>
    2 #include "add.h"
    3 
    4 int main(int argc, char *argv[])
    5 {
    6     printf("add = %d
    ", add(1, 1));
    7     return 0;
    8 }
    demo4.c

    通过file命令查看可执行程序的信息:

    运行结果:add = 2

    还可以通过“size [可执行程序]”命令,来查看程序的text段、data段、bss段的大小。

    在程序还没运行前,我们是可以确定它的text段、data段、bss段的大小。

  • 相关阅读:
    继续尝试,在Community Server添加一个页面二
    终于可以Blog了
    额的娘咧!从哪里下手啊!
    你可能肯定没有尝试中西合壁
    oracle11g常用bug故障排查步骤
    oracle11g dataguard完全手册2switchover
    oracle11g dataguard 完全手册
    oracle外部表 初探
    使用Deinstall专用工具删除Oracle Database
    oracle11g dataguard完全手册3failover &active dataguard(完)
  • 原文地址:https://www.cnblogs.com/yongqiang/p/6217962.html
Copyright © 2011-2022 走看看