• gcc 编译流程分析


    //test.c
    #include<stdio.h>
    int main()
    {
      int x=3,y=4;
      printf("x=%d y=%d
    ",x,y);
      return 0;
     }
    
    1:预处理阶段,对包含的头文件(#include)和宏定义(#define,#ifdef等)进行处理。在上述的代码处理过程中,编译器将包含的头文件stdio.h编译进来,并且让用户使用选项”-E“
    进行查看,该选项的作用是让gcc在预处理结束后停止编译过程。”.i“文件为已经过预处理的c程序。一下列出部分test.i文件的内容:
    # 1 "test.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "test.c"
    # 1 "/usr/include/stdio.h" 1 3 4
    # 28 "/usr/include/stdio.h" 3 4
    # 1 "/usr/include/features.h" 1 3 4
    # 361 "/usr/include/features.h" 3 4
    # 1 "/usr/include/sys/cdefs.h" 1 3 4
    # 365 "/usr/include/sys/cdefs.h" 3 4
    # 1 "/usr/include/bits/wordsize.h" 1 3 4
    # 366 "/usr/include/sys/cdefs.h" 2 3 4
    # 362 "/usr/include/features.h" 2 3 4
    # 385 "/usr/include/features.h" 3 4
    # 1 "/usr/include/gnu/stubs.h" 1 3 4
    ..................................................
    extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__)) ;
    
    
    extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__));
    # 938 "/usr/include/stdio.h" 3 4
    
    # 2 "test.c" 2
    int main()
    {
      int x=3,y=4;
      printf("x=%d y=%d
    ",x,y);
      return 0;
     }

    2:编译阶段
    接下来进行编译阶段,在这个阶段中,gcc首先要检查代码的规范性,是否有语法错误等,以确定代码实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。用户可以使用
    “-S”选项来进行查看,该选项只进行编译而不进行汇编,结果生成汇编代码。
    下面列出汇编代码test.s
        .file    "test.c"
        .section    .rodata
    .LC0:
        .string    "x=%d y=%d
    "
        .text
    .globl main
        .type    main, @function
    main:
        pushl    %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $3, 24(%esp)
        movl    $4, 28(%esp)
        movl    $.LC0, %eax
        movl    28(%esp), %edx
        movl    %edx, 8(%esp)
        movl    24(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        movl    $0, %eax
        leave
        ret
        .size    main, .-main
        .ident    "GCC: (GNU) 4.4.6 20120305 (Red Hat 4.4.6-4)"
        .section    .note.GNU-stack,"",@progbits
    3:汇编阶段
    汇编阶段把编译生成的.s文件转换成目标文件,读者在此使用选项“-c"就可以看到汇编代码已经转换成”.o“的二进制目标代码了!

    4:连接阶段
    在成功编译后,之后进入连接阶段。这里涉及到一个重要的概念:函数库。
    读者可以查看这个程序,在这个程序中并没有定义”printf"的函数实现,且在预编译中包含进来的“stdio"中也只有该函数的声明,而没有函数的实现,那么是在哪里实现了
    “printf”函数呢?最后的答案是系统把这些函数的实现都放在名为lib.so.6的库文件中去了,在没有特别指定时,gcc会到系统默认的搜索路径”/usr/bin"下进行查找,也就是链接到libc.so.6函数库中去,这样就能调用函数“printf"了,而这正是动态链接的作用。
    函数库有静态库和动态库两种。静态库是指编译链接时,将库文件的代码全部加入可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名通常为”.a“。动态库与之相反,在编译链接时并没有将库文件的代码加入到可执行文件中,而是在程序执行时加载库,这样可以节省系统的开销。一般动态库的后缀名为”.so“,如前面所述的lib.so.6就是动态库。gcc在编译时默认使用动态库。
    完了链接之后,gcc 就可以生成可执行文件。
    ”-I dir"选项可以在头文件的搜索路径列表中添加dir目录。由于linux中头文件都默认放到了“usr/include/"目录下,因此,当用户希望添加放置在其他位置的头文件时,就可以通过”-I dir"选项来指定,这样,gcc就会到相应的位置查找对应的目录。
    在include语句中,<>表示在标准路径中搜索头文件,“ ”表示在本目录中搜索。故在上例中,可把test.c中的#include<my.h>改为#include"my.h",就不需要加上“-I”选项了!!!!!!
    下面链接的时候出现错误的!!!!
  • 相关阅读:
    python学习之路01
    面向对象(2)__继承多态1
    面向对象(1)____私有公有 访问限制
    property
    yield理解
    列表推导式
    Django序列化1_基本的序列化和反序列化
    一些滑动操作
    装饰器
    django模板
  • 原文地址:https://www.cnblogs.com/leijiangtao/p/4390896.html
Copyright © 2020-2023  润新知