本文适用于Linux下开发初学者。本文初步讲解在Linux下如何使用GCC编译程序、简单生成静态库及动态库。
一、关于安装。一般系统默认是安装好编译器的,并且网络上有大量资料介绍不同发行版本下的安装问题,本文不再描述。
二、C编程中的文件后缀名介绍
    .a 静态库(打包文件)
    .c 未经过预处理的C源码
    .h C头文件
    .i 经过预处理的C源码
    .o 编译之后产生的目标文件
    .s 生成的汇编语言代码
    .so 动态库(动态链接库)
    解释:*.a是我们在编译过后用ar打包生成的静态库;*.c一般使我们自己编辑的代码,使我们劳动的结晶;*.h一般是我们手工生成的接口文件,如果愿意,也可在*.c完成后用GCC的选项-aux-info帮我们生成;*.i是经过预处理后的源码,是由GCC在选项-E编译下自动生成的文件;*.o是编译后产生的目标文件;*.s是GCC在选项-S编译下生成的汇编语言代码,对于性能要求很高的程序可以先生成汇编语言文件并对汇编做优化,然后用优化后的汇编生成目标文件并链接;*.so是动态库,通过GCC的-fpic -shared选项生成。
三、hello.c的编译过程
    本小节的演示都针对文件 hello.c 进行
  1. /*
  2.  * hello.c
  3.  */
  4.
  5.  #include <stdio.h>
  6.  int  main()
  7.  {
  8.    printf("hello, world!\n");
  9.    return 0;
  10.  }
1.直接生成可执行程序
  1. $ gcc -o hello hello.c
  2. $ ./hello
  3. hello, world!
  4.
  5. 如下编译方式结果相同:
  6. $ gcc hello.c -o hello
  7. $ ./hello
  8. hello, world!
  9.
  10. 如下编译方式有别于以上编译方案(具体查ELF和a.out文件格式差别的网络资料,对于此处结果是无任何区别的):
  11. $ gcc hello.c
  12. $ ./a.out
  13. hello, world!
2.生成预处理后的文件 hello.i
  1. $ gcc -E hello.c -o hello.i
  2. $ ls
  3. a.out  hello  hello.c  hello.i
  4. hello.i 就是新生成的文件
  5.
  6. 如下语句结果相同:
  7. $ gcc -E -o hello.i hello.c
  8.
  9. 如果不设定输出文件,则打印到标准终端,此时我们可以用 less 查看:
  10. $ gcc -E hello.c | less
  11. # 1 "hello.c"
  12. # 1 "<built-in>"
  13. # 1 "<command line>"
  14. # 1 "hello.c"
  15. # 1 "/usr/include/stdio.h" 1 3 4
  16. # 28 "/usr/include/stdio.h" 3 4
  17. # 1 "/usr/include/features.h" 1 3 4
  18. # 329 "/usr/include/features.h" 3 4
  19. ..............................
  20.
  21. 或者执行:
  22. $ gcc -E hello.c -o hello.i
  23. $ vi hello.i
  24.  1 # 1 "hello.c"
  25.  2 # 1 "<built-in>"
  26.  3 # 1 "<command line>"
  27.  4 # 1 "hello.c"
  28.  5 # 1 "/usr/include/stdio.h" 1 3 4
  29.  6 # 28 "/usr/include/stdio.h" 3 4
  30.  7 # 1 "/usr/include/features.h" 1 3 4
  31.  8 # 329 "/usr/include/features.h" 3 4
  32.
  33. .......... <;中间部分略> ......
............
  34.
  35. 929 # 844 "/usr/include/stdio.h" 3 4
  36. 930
  37. 931 # 2 "hello.c" 2
  38. 932
  39. 933 int main()
  40. 934 {
  41. 935        printf("hello, world!\n");
  42. 936
  43. 937        return 0;
  44. 938 }
  45.
  46. 可见,将近1000行的代码,我们的只占了最末8行。
  47.
3.生成汇编语言文件 hello.s
  1. $ gcc -S hello.c -o hello.s
  2. $ ls
  3. a.out  hello  hello.c  hello.i  hello.s
  4. hello.s就是新生成的文件
  5.
  6. 如下语句结果相同:
  7. $ gcc -S -o hello.s hello.c
  8.
  9. 如下语句结果相同:
  10. $ gcc -S hello.c
  11.
  12. 也可以采用前一步骤产生的中间文件生成汇编文件:
  13. $ gcc -S hello.i -o hello.s
  14. $ gcc -S -o hello.s hello.i
  15. $ gcc -S hello.i
  16.
  17.
  18. 生成的汇编部分代码如下:
  19. $ vi hello.s
  20.  1        .file  "hello.c"
  21.  2        .section        .rodata
  22.  3 .LC0:
  23.  4        .string "hello, world!"
  24.  5        .text
  25.  6 .globl main
  26.  7        .type  main, @function
  27.  8 main:
  28.  9        leal    4(%esp), %ecx
  29.  10        andl    $-16, %esp
  30.  11        pushl  -4(%ecx)
  31.  12        pushl  %ebp
  32. // 注释:如果你熟悉,就可以对部分汇编优化以达到更好效果。
4.生成目标文件 hello.o
  1. $ gcc -c hello.c -o hello.o
  2. $ ls
  3. a.out  hello  hello.c  hello.i  hello.o  hello.s
  4. hello.o就是新生成的目标文件:
  5.
  6. 如下语句结果相同:
  7. $ gcc -c -o hello.o hello.c
  8.
  9. 如下语句结果相同:
  10. $ gcc -c hello.c
  11.
  12. 也可以采用前面步骤产生的中间文件hello.i或hello.s来生成目标文件:
  13. $ gcc -c hello.i
  14. $ gcc -c hello.s
  15.
  16. 我们可以用 objdump 查看 hello.o 的二进制码:
  17. $ objdump -s hello.o
  18.
  19. hello.o:    file format elf32-i386
  20.
  21. Contents of section .text:
  22.  0000 8d4c2404 83e4f0ff 71fc5589 e55183ec  .L$.....q.U..Q..
  23.  0010 04c70424 00000000 e8fcffff ffb80000  ...$............
  24.  0020 000083c4 04595d8d 61fcc3            .....Y].a..   
  25. Contents of section .rodata:
  26.  0000 68656c6c 6f2c2077 6f726c64 2100      hello, world!.
  27. Contents of section ment:
  28.  0000 00474343 3a202847 4e552920 342e312e  .GCC: (GNU) 4.1.
  29.  0010 31203230 30373031 30352028 52656420  1 20070105 (Red
  30.  0020 48617420 342e312e 312d3532 2900      Hat 4.1.1-52).
5.采用中间级文件生成可执行程序
  1. $ gcc -o hello hello.i
  2. $ ./hello
  3. hello, world!
  4.
  5. $ gcc -o hello hello.s
  6. $ ./hello
  7. hello, world!
  8.
  9. $ gcc -o hello hello.o
  10. $ ./hello
  11. hello, world!
四、静态库的生成
    linux下静态库的生成比较方便。在生成目标文件后用 ar 打包即可。在中大型项目中一个模块一般会做成一个静态库,以方便管理、提高编
译、链接效率。
    本小节的展示针对 main.c、func1.c、func2.c三个文件
  1. /*
  2.  * main.c
  3.  */
  4. #include <stdio.h>
  5.
  6. extern int func1();
  7. extern int func2();
  8.
  9. int main()
  10. {
  11.        int i;
  12.
  13.        i = func1();
  14.        printf("func1 return = %d\n",i);
  15.
  16.        i = func2();
  17.        printf("func2 return = %d\n",i);
  18.
  19.        return 0;
  20. }
  21.
-----------------------------------------------------
  1. /*
  2.  * func1.c
  3.  */
  4. int func1()
  5. {
  6.        return 100;
  7. }
-----------------------------------------------------
  1. /*
  2.  * func2.c
  3.  */
  4. int func2()
  5. {
  6.        return 200;
  7. }
一下是编译指令:
  1. $ gcc -c func1.c
  2. $ gcc -c func2.c
  3. $ ls
  4. func1.c  func1.o  func2.c  func2.o  main.c
  5.
  6. func1.o 和 func2.o 是我们生成的目标文件。打包指令如下:
  7. $ ar -r libfunc.a func1.o func2.o
  8.
  9. 我们查看 libfunc.a 中的文件:
  10. $ ar -t libfunc.a
  11. func1.o
  12. func2.o
  13.
  14. 现在用静态库和 main.c 共同生成目标程序:
  15. $ gcc -o main main.c libfunc.a
  16. $ ./main
  17. func1 return = 100
  18. func2 return = 200
  19.
  20. 和我们的预期相符合。下面我们进入动态库。
  21.
linux下gcc编译的四个步骤
五、动态库的生成
    linux下动态库的生成通过GCC选项实现。案例程序和静态库中的相同。一下是操作指令:
  1. 首先我们生成目标文件,但是需要加编译器选项 -fpic 和链接器选项 -shared
  2. $ gcc -fpic -c func1.c
  3. $ gcc -fpic -c func2.c
  4. $ gcc -shared -o libfunc.so func1.o func2.o
  5. $ ls
  6. func1.c  func1.o  func2.c  func2.o  libfunc.so  main.c
  7.
  8. libfunc.so就是我们生成的目标动态库。我们用动态库和 main.c 生成目标程序:
  9. $ gcc -o main main.c -L. -lfunc
  10.
  11. 注意,我们用 -L. -lfunc 作为编译选项。-L. 表从当前目录查需要的动态库,-lfunc 是动态库的
调用规则。Linux系统下的动态库命名方式是 lib*.so,而在链接时表示位 -l* , *是自己起的库名。下面我们运行它:
  12.
  13. $ ./main
  14. ./main: error while loading shared libraries: libfunc.so: cannot open shared object file: No such file or directory
  15.
  16. 提示一个错误,指示无法到动态库。在linux下最方便的解决方案是拷贝libfunc.so到绝对目录 /lib 下。但是只有超级用户才有这个权限。另外一个方案是更改环境变量 LD_LIBRARY_PATH。如下:
  17. $ $ export LD_LIBRARY_PATH=`pwd`
  18. $ ./main
  19. func1 return = 100
  20. func2 return = 200
  21.
  22. 运行成功。现在我们更改动态库的函数而不重新链接。如下:
  23. 更改 func1.c 为:
  24. int func1()
  25. {
  26.        return 101;
  27. }
  28. 更改 func2.c 为:
  29. int func2()
  30. {
31.        return 202;
  32. }
  33. 重新生成库:
  34. $ gcc -fpic -shared func1.c func2.c -o libfunc.so
  35. $ ./main
  36. func1 return = 101
  37. func2 return = 202
  38.
  39. 可以看出,动态库已经更新了。
六、结束语
    本文简单介绍了linux下如何使用gcc进行编译程序、以及简单的静态、动态库的生成。静态库提供
了一种打包管理方案,而动态库使程序局部更新成为了可能,更重要的是,当有多份实例存在时,动态库可减小内存的消耗(只占用一份代码空间)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。