gcc编译问题

gcc avl.o hash.o list.o rb.o example.o -o 123.exehtml

多个.o输出 exe前端

 

-c和-o都是gcc编译器的可选参数。-c表示只编译(compile)源文件但不连接,会把.c或.cc的c源程序编译成目标文件,通常是.o文件。-o用于指定输出(out)文件名。不用-o的话,通常会在当前文件夹下生成默认的a.out文件做为可执行程序。java

 
  • gcc:GNU编译器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。GCC的初衷是为GNU操做系统专门编写的一款编译器。GNU系统是完全的自由软件。此处,“自由”的含义是它尊重用户的自由。node

  • 创做背景:GCC(GNU Compiler Collection,GNU编译器套件),是由 GNU 开发的编程语言编译器。它是以GPL许可证所发行的自由软件,也是 GNU计划的关键部分。GCC本来做为GNU操做系统的官方编译器,现已被大多数类Unix操做系统(如Linux、BSD、Mac OS X等)采纳为标准的编译器,GCC一样适用于微软的Windows。mysql

  • 基本用法:linux

  1. -o output_filename,肯定输出文件的名称为output_filename,同时这个名称不能和源文件同名。若是不给出这个选项,gcc就给出预设的可执行文件a.out。c++

  2. -O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程当中进行优化处理,这样产生的可执行文件的执行效率能够提升,可是,编译、连接的速度就相应地要慢一些。程序员

  3. -O2,比-O更好的优化编译、连接,固然整个编译、连接过程会更慢。web

  4. -Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程当中使用的参数。正则表达式

  5.  

 

-O参数只有5个级别,并非设置的越高越好(之前的GCC支持很高的级别,可是如今确实只有5级)

◆-O0:关闭全部优化选项

◆-O1:基本优化,编译器会生成更快的代码

◆-O2:-O1的升级版,推荐使用

◆-O3:这是目前最高的优化级别,它会使用更多的编译时间,虽然它生成的代码只会比-O2块一点点(GCC3.x中是这样的,可是在GCC4.x中有时候可能尚未-O2快),可是它会增大二进制文件的体积并让他们更消耗内存,所以在GCC4.x中-O3是不推荐的。

◆-Os:这个级别是用来优化代码尺寸的,他只是给一些CPU缓存或是磁盘空间小的机器使用

 

-O0: 无优化。

-O和-O1: 使用能减小目标代码尺寸以及执行时间而且不会使编译时间明显增长的优化。在编译大型程序的时候会显著增长编译时内存的使用。

-O2: 包含-O1的优化并增长了不须要在目标文件大小和执行速度上进行折衷的优化。编译器不执行循环展开以及函数内联。此选项将增长编译时间和目标文件的执行性能。

-Os: 能够当作 -O2.5,专门优化目标文件大小,执行全部的不增长目标文件大小的-O2优化选项,而且执行专门减少目标文件大小的优化选项。适用于磁盘空间紧张时使用。但有可能有未知的问题发生,何况目前硬盘容量很大,经常使用程序无必要使用。

-O3: 打开全部 -O2 的优化选项外增长 -finline-functions、-funswitch-loops、-fgcse-after-reload 优化选项。相对于 -O2 性能并未有较多提升,编译时间也最长,生成的目标文件也更大更占内存,有时性能不增反而下降,甚至产生不可预知的问题(包括错误),因此并不被大多数软件安装推荐,除非有绝对把握方可以使用此优化级别。

修改GCC编译参数,提升编译优化级别,此方法适用于全部经过GCC编译安装的程序,不止Nginx。稳妥起见用 -O2,这也是大多数软件编译推荐的优化级别。查看Nginx源码文件 auto/cc/gcc,搜索NGX_GCC_OPT,默认GCC编译参数为-O,能够直接修改内容为NGX_GCC_OPT="-O2"或者在 ./configure配置时添加--with-cc-opt='-O2'选项。

 
来源:CSDN
原文:https://blog.csdn.net/xiaoyi23000/article/details/79789529
 

 

使用GCC编译一个.c文件影藏了哪些过程?


GCC四步详解
第一步:预处理(也叫预编译)
        gcc -E  hello.c  -o hello.i
        或者 cpp hello.c > hello.i     【cpp是预编译器】
        将全部#define删除,而且展开全部的宏定义
        处理全部的条件预编译指令,如#if #ifdef  #undef  #ifndef  #endif #elif
        处理#include,将包含的文件插入到此处,这是一个递归的过程
        删除全部注释   //   /* */
        添加行号和文件名标识,以便于编译时产生的错误警告能显示行号
        保留#pragma编译器指令
第二步:编译
        gcc  -S  hello.i   -o  hello.s
        将预处理完的.i文件进行一系列的词法分析、语法分析、语义分析及优
        化后生成响应的汇编代码文件,这是整个程序构建的最核心的部分,也是最复杂的部分

第三步:汇编
        gcc  -c  hello.s  -o  hello.o或者 as  hello.s -o  hello.o
        汇编是将第二步生成的汇编代码编程机器可执行的指令,每个汇编语句几乎都对应一条机器指令


第四步:连接

         连接动态库和静态库

 

生成的目标文件有什么,什么是目标文件?
目标文件就是源代码通过编译后但未进行连接的那些中间文件
Linux下的 .o文件就是目标文件,目标文件和可执行文件内容和
格式几乎都同样,因此咱们能够广义地将目标文件和可执行文化
当作一类型文件。他们都是按照ELF文件格式存储的


Linux下有哪些ELF类型的文件?
.o文件、可执行文件、核心转储文件(core dump)、.so文件(动态链
连接库)


可执行文件的概貌详解
File  Header.text section.data section.bss section
文件头(File Header)
描述了整个文件的文件属性,包括目标文件是否可执行、是静态连接还 是动
态连接及入口地址、目标硬件、目标操做系统等信息、段表(描述文件中各
个段的偏移位置及属性等)
代码段(.text)
存放了程序源代码编译后生成的机器指令
数据段(.data)
存放已初始化的全局静态与非静态变量和已初始化的局部静态变量
.bss段
存放未初始化的全局变量(全局静态和非静态变量)和局部静态变量
可是.bss段只是为这些变量预留位置而已,并无内容,因此这些变量
在.bss段中也不占据空间


深刻挖掘 .o文件


使用命令:


objdump  -h  xxxx.o

        打印主要段的信息

objdump  -x  xxxx.o 
            打印更多的详细信息
objdump  -s  xxx.o
            将全部段的内容以16进制方式打印出来
objdump  -d  xxx.o  或者-S
            将全部包含指令的段反汇编
objdump   -t   xxx.o
            查看全部的符号以及他们所在段
readelf  -h   xxx.o
            查看.o文件的文件头详细信息
readelf   -S   xxx.o
            显示.o文件中的全部段,即查看段表
size xxx.o
            查看.o文件中各个段所占大小
nm xxx.o 
            查看.o文件中全部的符号


使用命令gcc -c test.c编译下面这个test.c程序生成test.o文件,而后查看test.o文件结构


test.c

/* this is a test code */
/* test.c */

int printf(const char *format, ...);

int g_var2 = 10;
int g_var2;

void func(int i)
{
printf("%d\n",i);
}

int main(void)
{
static int static_var1 = 20;
static int static_var2;

int var3 = 1;
int var4;
func(static_var1 + static_var2 + var3 + var4);
return var3;
}

而后查看生成的test.o文件的结构
objdump -h test.o

行:
    .text  :代码段(存放函数的二进制机器指令)
    .data :数据段(存已初始化的局部/全局静态变量、未初始化的全局静态变量)
    .bss  :bss段(声明未初始化变量所占大小)
    .rodata :只读数据段(存放 " " 引住的只读字符串)
    .comment :注释信息段
    .node.GUN-stack :堆栈提示段
列:
    Size:段的长度
    File Off :段的所在位置(即距离文件头的偏移位置)
段的属性:
    CONTENTS:表示该段在文件中存在
    ALLOC :表示只分配了大小,但没有存内容


关于.bss段
咱们说.bss段是存放未初始化的全局变量(静态与非静态)和局部静态变量的
因此咱们程序中的g_var2和stactic_var2应该都在.bss段中被预留位置,因此
.bss段的size应该是8个字节,可是结果倒是4个字节,怎么回事呢?
这就是不用的编译器实现不同的缘由了,有些编译器会将未初始化的全局非静态变量放在.bss段,有些则不放,只是预留一个未定义的全局变量符号,等到最终连接成可执行文件的时候再在.bss段分配空间。而个人编译器是没有将g_var2(全局未初始化的非静态变量)放在任何段
下面让咱们真正的查看一下g_var2
首先,咱们使用  readelf -S  test.o  查看段表(主要为了查看每一个段的段号)


而后咱们再使用 readelf -s  test.o看一下符号表(咱们定义的变量名都是符号,包括函数名)
符号表里会显示这个符号所在的位置

 


咱们看到static_var1和g_var1所在段的段号为3(3是.data段),static_var2所在段的段号为4(4是.bss段),而g_var2却没有被放入任何一个段,只是用COM标记了一下,那这个COM表示什么意思呢?COM标记的符号被称为弱符号,一个变量名是弱符号,则这个变量的大小在编译的时候不能被肯定,而在连接以后才能肯定该变量的大小。test.o文件在连接以后,g_var2会被放入.bss段(固然,也只是说明g_var2所须要的空间大小,并不会存放内容),而在程序运行的时候g_var2这样的变量才会真正去占用内存空间


强制将某变量或者某函数放入某个段
__attribute__((section(".data")))  int   g_var2;   //强制将g_var2放入.data段中

 

各类变量所在位置总结
    全局已初始化非静态变量、局部已初始化静态变量会被放入.data段
    全局未初始化静态变量会被放入.bss段
    全图未初始化非静态变量不会被放入任何一个段,只是用COM标记一下
 
来源:CSDN
原文:https://blog.csdn.net/gt1025814447/article/details/80442673
 

 

版本] -0.13  
[声明]  
这篇文档是个人关于gcc参数的笔记,我很怀念dos年代我用小本子,纪录任何的dos 命令的参数.哈哈,下面的东西可能也不是很全面,我参考了不少的书,和gcc的帮助.不全的缘由是,有可能我尚未看到这个参数,另外一种缘由是,我可能还不会用他 可是,我会慢慢的补齐的.哈哈 假如您要转在本文章请保留我email(pianopan@beeship.com)和文章的全面性.  
[介绍]  
gcc and g++分别是gnu的c & c++编译器 gcc/g++在执行编译工做的时候,总共须要4步  
1.预处理,生成.i的文档[预处理器cpp]  
2.将预处理后的文档不转换成汇编语言,生成文档.s[编译器egcs]  
3.有汇编变为目标代码(机器代码)生成.o的文档[汇编器as]  
4.链接目标代码,生成可执行程式[连接器ld] 

[参数详解]  
-x language filename  
  设定文档所使用的语言,使后缀名无效,对之后的多个有效.也就是根据约定C语言的后缀名称是.c的,而C++的后缀名是.C或.cpp,假如您很个性,决定您的C代码文档的后缀名是.pig 哈哈,那您就要用这个参数,这个参数对他后面的文档名都起做用,除非到了下一个参数的使用。  
  可以使用的参数吗有下面的这些  
  `c', `objective-c', `c-header', `c++', `cpp-output', `assembler', and `assembler-with-cpp'.  
  看到英文,应该可以理解的。  
  例子用法:  
  gcc -x c hello.pig  
    
-x none filename  
  关掉上一个选项,也就是让gcc根据文档名后缀,自动识别文档类型  
  例子用法:  
  gcc -x c hello.pig -x none hello2.c  
    
-c  
  只激活预处理,编译,和汇编,也就是他只把程式作成obj文档  
  例子用法:  
  gcc -c hello.c  
  他将生成.o的obj文档 

-S  
  只激活预处理和编译,就是指把文档编译成为汇编代码。  
  例子用法  
  gcc -S hello.c  
  他将生成.s的汇编代码,您可以用文本编辑器察看 

-E  
  只激活预处理,这个不生成文档,您须要把他重定向到一个输出文档里面.  
  例子用法:  
  gcc -E hello.c > pianoapan.txt  
  gcc -E hello.c | more  
  慢慢看吧,一个hello word 也要和处理成800行的代码 

-o  
  定制目标名称,缺省的时候,gcc 编译出来的文档是a.out,很难听,假如您和我有同感,改掉他,哈哈  
  例子用法  
  gcc -o hello.exe hello.c (哦,windows用习惯了)  
  gcc -o hello.asm -S hello.c 

-pipe  
  使用管道代替编译中临时文档,在使用非gnu汇编工具的时候,可能有些问题  
  gcc -pipe -o hello.exe hello.c 

-ansi  
  关闭gnu c中和ansi c不兼容的特性,激活ansi c的专有特性(包括禁止一些asm inline typeof关键字,连同UNIX,vax等预处理宏, 

-fno-asm  
  此选项实现ansi选项的功能的一部分,他禁止将asm,inline和typeof用做关键字。       
-fno-strict-prototype  
  只对g++起做用,使用这个选项,g++将对不带参数的函数,都认为是没有显式的对参数的个数和类型说明,而不是没有参数.  
  而gcc不管是否使用这个参数,都将对没有带参数的函数,认为城没有显式说明的类型  
    
-fthis-is-varialble  
  就是向传统c++看齐,可以使用this当通常变量使用.  
    
-fcond-mismatch  
  容许条件表达式的第二和第三参数类型不匹配,表达式的值将为void类型  
    
-funsigned-char  
-fno-signed-char  
-fsigned-char  
-fno-unsigned-char  
  这四个参数是对char类型进行配置,决定将char类型配置成unsigned char(前两个参数)或 signed char(后两个参数)  
    
-include file  
  包含某个代码,简单来讲,就是便以某个文档,须要另外一个文档的时候,就可以用他设定,功能就至关于在代码中使用#include<filename>  
  例子用法:  
  gcc hello.c -include /root/pianopan.h  
    
-imacros file  
  将file文档的宏,扩展到gcc/g++的输入文档,宏定义自己并不出如今输入文档中  
    
-Dmacro  
  至关于C语言中的#define macro  
    
-Dmacro=defn  
  至关于C语言中的#define macro=defn  
    
-Umacro  
  至关于C语言中的#undef macro 

-undef  
  取消对任何非标准宏的定义  
    
-Idir  
  在您是用#include"file"的时候,gcc/g++会先在当前目录查找您所定制的头文档,假如没有找到,他回到缺省的头文档目录找,假如使用-I定制了目录,他  
  回先在您所定制的目录查找,而后再按常规的顺序去找.  
  对于#include<file>,gcc/g++会到-I定制的目录查找,查很难找到,而后将到系统的缺省的头文档目录查找  
    
-I-  
  就是取消前一个参数的功能,因此通常在-Idir以后使用  
    
-idirafter dir  
  在-I的目录里面查找失败,讲到这个目录里面查找.  
    
-iprefix prefix  
-iwithprefix dir  
  通常一块儿使用,当-I的目录查找失败,会到prefix+dir下查找  
    
-nostdinc  
  使编译器再也不系统缺省的头文档目录里面找头文档,通常和-I联合使用,明确限定头文档的位置  
    
-nostdin C++  
  规定不在g++指定的标准路经中搜索,但仍在其余路径中搜索,.此选项在创libg++库使用  
    
-C  
  在预处理的时候,不删除注释信息,通常和-E使用,有时候分析程式,用这个很方便的  
    
-M  
  生成文档关联的信息。包含目标文档所依赖的任何源代码您可以用gcc -M hello.c来测试一下,很简单。  
    
-MM  
  和上面的那个相同,可是他将忽略由#include<file>形成的依赖关系。  
    
-MD  
  和-M相同,可是输出将导入到.d的文档里面  
    
-MMD  
  和-MM相同,可是输出将导入到.d的文档里面  
    
-Wa,option  
  此选项传递option给汇编程式;假如option中间有逗号,就将option分红多个选项,而后传递给会汇编程式  
    
-Wl.option  
  此选项传递option给链接程式;假如option中间有逗号,就将option分红多个选项,而后传递给会链接程式.  
   

-llibrary  
  定制编译的时候使用的库  
  例子用法  
  gcc -lcurses hello.c  
  使用ncurses库编译程式  
    
-Ldir  
  定制编译的时候,搜索库的路径。好比您本身的库,可以用他定制目录,否则  
  编译器将只在标准库的目录找。这个dir就是目录的名称。  
    
-O0  
-O1  
-O2  
-O3  
  编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高       
-g  
  只是编译器,在编译的时候,产生调试信息。  
    
-gstabs  
  此选项以stabs格式声称调试信息,可是不包括gdb调试信息.  
    
-gstabs+  
  此选项以stabs格式声称调试信息,而且包含仅供gdb使用的额外调试信息.  
    
-ggdb  
  此选项将尽量的生成gdb的可以使用的调试信息. 

-static  
  此选项将禁止使用动态库,因此,编译出来的东西,通常都很大,也无需什么  
动态链接库,就可以运行. 

-share  
  此选项将尽可能使用动态库,因此生成文档比较小,可是须要系统由动态库. 

-traditional  
  试图让编译器支持传统的C语言特性 

 

 

gcc提供了大量的警告选项,对代码中可能存在的问题提出警 告,一般能够使用-Wall来开启如下警告:
           -Waddress -Warray-bounds (only with -O2) -Wc++0x-compat
           -Wchar-subscripts -Wimplicit-int -Wimplicit-function-declaration
           -Wcomment -Wformat -Wmain (only for C/ObjC and unless
           -ffreestanding) -Wmissing-braces -Wnonnull -Wparentheses
           -Wpointer-sign -Wreorder -Wreturn-type -Wsequence-point
           -Wsign-compare (only in C++) -Wstrict-aliasing -Wstrict-overflow=1
           -Wswitch -Wtrigraphs -Wuninitialized (only with -O1 and above)
           -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value
           -Wunused-variable
unused-function:警告声明可是没有定义的static函数;
unused- label:声明可是未使用的标签;
unused-parameter:警告未使用的函数参数;
unused-variable:声明但 是未使用的本地变量;
unused-value:计算了可是未使用的值;
format:printf和scanf这样的函数中的格式字符 串的使用不当;
implicit-int:未指定类型;
implicit-function:函数在声明前使用;
char- subscripts:使用char类做为数组下标(由于char多是有符号数);
missingbraces:大括号不匹配;
parentheses: 圆括号不匹配;
return-type:函数有无返回值以及返回值类型不匹配;
sequence-point:违反顺序点的代码,好比 a[i] = c[i++];
switch:switch语句缺乏default或者switch使用枚举变量为索引时缺乏某个变量的case;
strict- aliasing=n:使用n设置对指针变量指向的对象类型产生警告的限制程度,默认n=3;只有在-fstrict-aliasing设置的状况下有 效;
unknow-pragmas:使用未知的#pragma指令;
uninitialized:使用的变量为初始化,只在-O2时有 效;

如下是在-Wall中不会激活的警告选项:
cast-align:当指针进行类型转换后有内存对齐要求更严格时发出警告;
sign- compare:当使用signed和unsigned类型比较时;
missing-prototypes:当函数在使用前没有函数原型时;
packed:packed 是gcc的一个扩展,是使结构体各成员之间不留内存对齐所需的 空 间,有时候会形成内存对齐的问题;
padded:也是gcc的扩展,使结构体成员之间进行内存对齐的填充,会 形成结构体体积增大.
unreachable-code:有不会执行的代码时.
inline:当inline函数再也不保持inline时 (好比对inline函数取地址);
disable-optimization:当不能执行指定的优化时.(须要太多时间或 系统资源).
能够使用 -Werror时全部的警告都变成错误,使出现警告时也中止编译.须要和指定警告的参数一块儿使用.

优化:
gcc默认提供了5级优 化选项的集合:
-O0:无优化(默认)
-O和-O1:使用能减小目标 文 件大小以及执行时间而且不会使编译时间明显增长的优化.在编译大型程序的时候会显著增长编译时内存的使用.
-O2: 包含-O1的优化并增长了不须要在目标文件大小和执行速度上进行折衷的优化.编译器不执行循环展开以及函数内联.此选项将增长编译时间和目标文件的执行性 能.
-Os:专门优化目标文件大小,执行全部的不增长目标文件大小的-O2优化选项.而且执行专门减少目标文件大小的优化选项.
-O3: 打开全部-O2的优化选项而且增长 -finline-functions, -funswitch-loops,-fpredictive-commoning, -fgcse-after-reload and -ftree-vectorize优化选项.

-O1包含的选项-O1一般能够安全的和调试的选项一块儿使用:
           -fauto-inc-dec -fcprop-registers -fdce -fdefer-pop -fdelayed-branch
           -fdse -fguess-branch-probability -fif-conversion2 -fif-conversion
           -finline-small-functions -fipa-pure-const -fipa-reference
           -fmerge-constants -fsplit-wide-types -ftree-ccp -ftree-ch
           -ftree-copyrename -ftree-dce -ftree-dominator-opts -ftree-dse
           -ftree-fre -ftree-sra -ftree-ter -funit-at-a-time

如下全部的优化选项须要在名字 前加上-f,若是不须要此选项能够使用-fno-前缀
defer-pop:延迟到只在必要时从函数参数栈中pop参数;
thread- jumps:使用跳转线程优化,避免跳转到另外一个跳转;
branch-probabilities:分支优化;
cprop- registers:使用寄存器之间copy-propagation传值;
guess-branch-probability:分支预测;
omit- frame-pointer:可能的状况下不产生栈帧;

-O2:如下是-O2在-O1基础上增长的优化选项:
           -falign-functions  -falign-jumps -falign-loops  -falign-labels
           -fcaller-saves -fcrossjumping -fcse-follow-jumps  -fcse-skip-blocks
           -fdelete-null-pointer-checks -fexpensive-optimizations -fgcse
           -fgcse-lm -foptimize-sibling-calls -fpeephole2 -fregmove
           -freorder-blocks  -freorder-functions -frerun-cse-after-loop
           -fsched-interblock  -fsched-spec -fschedule-insns
           -fschedule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-pre
           -ftree-vrp
cpu架构的优化选项,一般是-mcpu(将被取消);-march,-mtune

Debug选项:
在 gcc编译源代码时指定-g选项能够产生带有调试信息的目标代码,gcc能够为多个不一样平台上帝不一样调试器提供调试信息,默认gcc产生的调试信息是为 gdb使用的,能够使用-gformat 指定要生成的调试信息的格式以提供给其余平台的其余调试器使用.经常使用的格式有
-ggdb:生成gdb专 用的调试信息,使用最适合的格式(DWARF 2,stabs等)会有一些gdb专用的扩展,可能形成其余调试器没法运行.
-gstabs:使用 stabs格式,不包含gdb扩展,stabs经常使用于BSD系统的DBX调试器.
-gcoff:产生COFF格式的调试信息,经常使用于System V下的SDB调试器;
-gxcoff:产生XCOFF格式的调试信息,用于IBM的RS/6000下的DBX调试器;
-gdwarf- 2:产生DWARF version2 的格式的调试信息,经常使用于IRIXX6上的DBX调试器.GCC会使用DWARF version3的一些特性.
可 以指定调试信息的等级:在指定的调试格式后面加上等级:
如: -ggdb2 等,0表明不产生调试信息.在使用-gdwarf-2时由于最先的格式为-gdwarf2会形成混乱,因此要额外使用一个-glevel来指定调试信息的 等级,其余格式选项也能够另外指定等级.

gcc能够使用-p选项指定生成信息以供porf使用.

 

GCC经常使用选项

选项 含义
--help 
--target-help
显示 gcc 帮助说明。‘target-help’是显示目标机器特定的命令行选项。
--version 显示 gcc 版本号和版权信息 。
-ooutfile 输出到指定的文件。
-xlanguage 指明使用的编程语言。容许的语言包括:c c++ assembler none 。 ‘none’意味着恢复默认行为,即根据文件的扩展名猜想源文件的语言。
-v 打印较多信息,显示编译器调用的程序。
-### 与 -v 相似,但选项被引号括住,而且不执行命令。
-E 仅做预处理,不进行编译、汇编和连接。如上图所示。
-S 仅编译到汇编语言,不进行汇编和连接。如上图所示。
-c 编译、汇编到目标代码,不进行连接。如上图所示。
-pipe 使用管道代替临时文件。
-combine 将多个源文件一次性传递给汇编器。

 

 

3 其余GCC选项

更多有用的GCC选项:

 

命令 描述
-l library
-llibrary
进行连接时搜索名为library的库。
例子: $ gcc test.c -lm -o test
-Idir dir加入到搜索头文件的路径列表中。
例子: $ gcc test.c -I../inc -o test
-Ldir dir加入到搜索库文件的路径列表中。
例子: $ gcc -I/home/foo -L/home/foo -ltest test.c -o test
-Dname 预约义一个名为name的宏,值为1。
例子: $ gcc -DTEST_CONFIG test.c -o test
-Dname=definition 预约义名为name,值为definition的宏。
-ggdb 
-ggdblevel
为调试器 gdb 生成调试信息。level能够为1,2,3,默认值为2。
-g 
-glevel
生成操做系统本地格式的调试信息。-g 和 -ggdb 并不太相同, -g 会生成 gdb 以外的信息。level取值同上。
-s 去除可执行文件中的符号表和重定位信息。用于减少可执行文件的大小。
-M 告诉预处理器输出一个适合make的规则,用于描述各目标文件的依赖关系。对于每一个 源文件,预处理器输出 一个make规则,该规则的目标项(target)是源文件对应的目标文件名,依赖项(dependency)是源文件中 `#include引用的全部文件。生成的规则可 以是单行,但若是太长,就用`\'-换行符续成多行。规则 显示在标准输出,不产生预处理过的C程序。
-C 告诉预处理器不要丢弃注释。配合`-E'选项使用。
-P 告诉预处理器不要产生`#line'命令。配合`-E'选项使用。
-static 在支持动态连接的系统上,阻止链接共享库。该选项在其它系统上 无效。
-nostdlib 不链接系统标准启动文件和标准库文件,只把指定的文件传递给链接器。
Warnings  
-Wall 会打开一些颇有用的警告选项,建议编译时加此选项。
-W 
-Wextra
打印一些额外的警告信息。
-w 禁止显示全部警告信息。
-Wshadow 当一个局部变量遮盖住了另外一个局部变量,或者全局变量时,给出警告。颇有用的选项,建议打开。 -Wall 并不会打开此项。
-Wpointer-arith 对函数指针或者void *类型的指针进行算术操做时给出警告。也颇有用。 -Wall 并不会打开此项。
-Wcast-qual 当强制转化丢掉了类型修饰符时给出警告。 -Wall 并不会打开此项。
-Waggregate-return 若是定义或调用了返回结构体或联合体的函数,编译器就发出警告。
-Winline 不管是声明为 inline 或者是指定了-finline-functions 选项,若是某函数不能内联,编译器都将发出警告。若是你的代码含有不少 inline 函数的话,这是颇有用的选项。
-Werror 把警告看成错误。出现任何警告就放弃编译。
-Wunreachable-code 若是编译器探测到永远不会执行到的代码,就给出警告。也是比较有用的选项。
-Wcast-align 一旦某个指针类型强制转换致使目标所需的地址对齐增长时,编译器就发出警告。
-Wundef 当一个没有定义的符号出如今 #if 中时,给出警告。
-Wredundant-decls 若是在同一个可见域内某定义屡次声明,编译器就发出警告,即便这些重复声明有效而且毫无差异。
Optimization  
-O0 禁止编译器进行优化。默认为此项。
-O 
-O1
尝试优化编译时间和可执行文件大小。
-O2 更多的优化,会尝试几乎所有的优化功能,但不会进行“空间换时间”的优化方法。
-O3 在 -O2 的基础上再打开一些优化选项:-finline-functions, -funswitch-loops 和 -fgcse-after-reload 。
-Os 对生成文件大小进行优化。它会打开 -O2 开的所有选项,除了会那些增长文件大小的。
-finline-functions 把全部简单的函数内联进调用者。编译器会探索式地决定哪些函数足够简单,值得作这种内联。
-fstrict-aliasing 施加最强的别名规则(aliasing rules)。
Standard  
-ansi 支持符合ANSI标准的C程序。这样就会关闭GNU C中某些不兼容ANSI C的特性。
-std=c89 
-iso9899:1990
指明使用标准 ISO C90 做为标准来编译程序。
-std=c99 
-std=iso9899:1999
指明使用标准 ISO C99 做为标准来编译程序。
-std=c++98 指明使用标准 C++98 做为标准来编译程序。
-std=gnu9x 
-std=gnu99
使用 ISO C99 再加上 GNU 的一些扩展。
-fno-asm 不把asm, inline或typeof看成关键字,所以这些词能够用作标识符。用 __asm__, __inline__和__typeof__可以替代它们。 `-ansi' 隐含声明了`-fno-asm'。
-fgnu89-inline 告诉编译器在 C99 模式下看到 inline 函数时使用传统的 GNU 句法。
C options  
-fsigned-char 
-funsigned-char
把char定义为有/无符号类型,如同signed char/unsigned char。
-traditional 尝试支持传统C编译器的某些方面。详见GNU C手册。
-fno-builtin 
-fno-builtin-function
不接受没有 __builtin_ 前缀的函数做为内建函数。
-trigraphs 支持ANSI C的三联符( trigraphs)。`-ansi'选项隐含声明了此选项。
-fsigned-bitfields 
-funsigned-bitfields
若是没有明确声明`signed'或`unsigned'修饰符,这些选项用来定义有符号位域或无符号位域。缺省状况下,位域是有符号的,由于它们继承的基本整数类型,如int,是有符号数。
-Wstrict-prototypes 若是函数的声明或定义没有指出参数类型,编译器就发出警告。颇有用的警告。
-Wmissing-prototypes 若是没有预先声明就定义了全局函数,编译器就发出警告。即便函数定义自身提供了函数原形也会产生这个警告。这个选项 的目的是检查没有在头文件中声明的全局函数。
-Wnested-externs 若是某extern声明出如今函数内部,编译器就发出警告。
C++ options  
-ffor-scope 从头开始执行程序,也容许进行重定向。
-fno-rtti 关闭对 dynamic_cast 和 typeid 的支持。若是你不须要这些功能,关闭它会节省一些空间。
-Wctor-dtor-privacy 当一个类没有用时给出警告。由于构造函数和析构函数会被看成私有的。
-Wnon-virtual-dtor 当一个类有多态性,而又没有虚析构函数时,发出警告。-Wall会开启这个选项。
-Wreorder 若是代码中的成员变量的初始化顺序和它们实际执行时初始化顺序不一致,给出警告。
-Wno-deprecated 使用过期的特性时不要给出警告。
-Woverloaded-virtual 若是函数的声明隐藏住了基类的虚函数,就给出警告。
Machine Dependent Options (Intel)  
-mtune=cpu-type 为指定类型的 CPU 生成代码。cpu-type能够是:i386,i486,i586,pentium,i686,pentium4 等等。
-msse 
-msse2 
-mmmx 
-mno-sse 
-mno-sse2 
-mno-mmx
使用或者不使用MMX,SSE,SSE2指令。
-m32 
-m64
生成32位/64位机器上的代码。
-mpush-args 
-mno-push-args
(不)使用 push 指令来进行存储参数。默认是使用。
-mregparm=num 当传递整数参数时,控制所使用寄存器的个数。

 

 

 

 

 

让咱们先看看 Makefile 规则中的编译命令一般是怎么写的。

大多数软件包遵照以下约定俗成的规范:

#1,首先从源代码生成目标文件(预处理,编译,汇编),"-c"选项表示不执行连接步骤。
$(CC) $(CPPFLAGS) $(CFLAGS) example.c   -c   -o example.o
#2,而后将目标文件链接为最终的结果(链接),"-o"选项用于指定输出文件的名字。
$(CC) $(LDFLAGS) example.o   -o example

#有一些软件包一次完成四个步骤:
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) example.c   -o example

固然也有少数软件包不遵照这些约定俗成的规范,好比:

#1,有些在命令行中漏掉应有的Makefile变量(注意:有些遗漏是故意的)
$(CC) $(CFLAGS) example.c    -c   -o example.o
$(CC) $(CPPFLAGS) example.c  -c   -o example.o
$(CC) example.o   -o example
$(CC) example.c   -o example
#2,有些在命令行中增长了没必要要的Makefile变量
$(CC) $(CFLAGS) $(LDFLAGS) example.o   -o example
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) example.c   -c   -o example.o

固然还有极个别软件包彻底是"胡来":乱用变量(增长没必要要的又漏掉了应有的)者有之,不用$(CC)者有之,不一而足.....

尽管将源代码编译为二进制文件的四个步骤由不一样的程序(cpp,gcc/g++,as,ld)完成,可是事实上 cpp, as, ld 都是由 gcc/g++ 进行间接调用的。换句话说,控制了 gcc/g++ 就等于控制了全部四个步骤。从 Makefile 规则中的编译命令能够看出,编译工具的行为全靠 CC/CXX CPPFLAGS CFLAGS/CXXFLAGS LDFLAGS 这几个变量在控制。固然理论上控制编译工具行为的还应当有 AS ASFLAGS ARFLAGS 等变量,可是实践中基本上没有软件包使用它们。

那么咱们如何控制这些变量呢?一种简易的作法是首先设置与这些 Makefile 变量同名的环境变量并将它们 export 为全局,而后运行 configure 脚本,大多数 configure 脚本会使用这同名的环境变量代替 Makefile 中的值。可是少数 configure 脚本并不这样作(好比GCC-3.4.6和Binutils-2.16.1的脚本就不传递LDFLAGS),你必须手动编辑生成的 Makefile 文件,在其中寻找这些变量并修改它们的值,许多源码包在每一个子文件夹中都有 Makefile 文件,真是一件很累人的事!

CC 与 CXX

这是 C 与 C++ 编译器命令。默认值通常是 "gcc" 与 "g++"。这个变量原本与优化没有关系,可是有些人由于担忧软件包不遵照那些约定俗成的规范,惧怕本身苦心设置的 CFLAGS/CXXFLAGS/LDFLAGS 之类的变量被忽略了,而索性将本来应当放置在其它变量中的选项一股老儿塞到 CC 或 CXX 中,好比:CC="gcc -march=k8 -O2 -s"。这是一种怪异的用法,本文不提倡这种作法,而是提倡按照变量原本的含义使用变量。

CPPFLAGS

这是用于预处理阶段的选项。不过可以用于此变量的选项,看不出有哪一个与优化相关。若是你实在想设一个,那就使用下面这两个吧:

-DNDEBUG
"NDEBUG"是一个标准的 ANSI 宏,表示不进行调试编译。
-D_FILE_OFFSET_BITS=64
大多数包使用这个来提供大文件(>2G)支持。

CFLAGS 与 CXXFLAGS

CFLAGS 表示用于 C 编译器的选项,CXXFLAGS 表示用于 C++ 编译器的选项。这两个变量实际上涵盖了编译和汇编两个步骤。大多数程序和库在编译时默认的优化级别是"2"(使用"-O2"选项)而且带有调试符号来编 译,也就是 CFLAGS="-O2 -g", CXXFLAGS=$CFLAGS 。事实上,"-O2"已经启用绝大多数安全的优化选项了。另外一方面,因为大部分选项能够同时用于这两个变量,因此仅在最后讲述只能用于其中一个变量的选 项。[提醒]下面所列选项皆为非默认选项,你只要按需添加便可。

先说说"-O3"在"-O2"基础上增长的几项:

-finline-functions
容许编译器选择某些简单的函数在其被调用处展开,比较安全的选项,特别是在CPU二级缓 存较大时建议使用。
-funswitch-loops
将循环体中不改变值的变量移动到循环体以外。
-fgcse-after-reload
为了清除多余的溢出,在重载以后执行一个额外的载入消除步骤。

另外:

-fomit-frame-pointer
对于不须要栈指针的函数就不在寄存器中保存指针,所以能够忽略存储和检索地址的代 码,同时对许多函数提供一个额外的寄存器。全部"-O"级别都打开它,但仅在调试器能够不依靠栈指针运行时才有效。在AMD64平台上此选项默认打开,但 是在x86平台上则默认关闭。建议显式的设置它。
-falign-functions=N
-falign-jumps=N
-falign-loops=N
-falign-labels=N
这 四个对齐选项在"-O2"中打开,其中的根据不一样的平台N使用不一样的默认值。若是你想指定不一样于默认值的N,也能够单独指定。好比,对于L2- cache>=1M的cpu而言,指定 -falign-functions=64 可能会得到更好的性能。建议在指定了 -march 的时候不明确指定这里的值。

调试选项:

-fprofile-arcs
在使用这一选项编译程序并运行它以建立包含每一个代码块的执行次数的文件后,程序能够再次使用 -fbranch-probabilities 编译,文件中的信息能够用来优化那些常常选取的分支。若是没有这些信息,gcc将猜想哪一个分支将被常常运行以进行优化。这类优化信息将会存放在一个以源文 件为名字的并以".da"为后缀的文件中。

全局选项:

-pipe
在编译过程的不一样阶段之间使用管道而非临时文件进行通讯,能够加快编译速度。建议使用。

目录选项:

--sysroot=dir
将dir做为逻辑根目录。好比编译器一般会在 /usr/include 和 /usr/lib 中搜索头文件和库,使用这个选项后将在 dir/usr/include 和 dir/usr/lib 目录中搜索。若是使用这个选项的同时又使用了 -isysroot 选项,则此选项仅做用于库文件的搜索路径,而 -isysroot 选项将做用于头文件的搜索路径。这个选项与优化无关,可是在 CLFS 中有着神奇的做用。

代码生成选项:

-fno-bounds-check
关闭全部对数组访问的边界检查。该选项将提升数组索引的性能,但当超出数组边界时,可能会 形成不可接受的行为。
-freg-struct-return
若是struct和union足够小就经过寄存器返回,这将提升较小结构的效率。若是 不够小,没法容纳在一个寄存器中,将使用内存返回。建议仅在彻底使用GCC编译的系统上才使用。
-fpic
生成可用于共享库的位置独立代码。全部的内部寻址均经过全局偏移表完成。要肯定一个地址,须要将代码自身的内存位置 做为表中一项插入。该选项产生能够在共享库中存放并从中加载的目标模块。
-fstack-check
为防止程序栈溢出而进行必要的检测,仅在多线程环境中运行时才可能须要它。
-fvisibility=hidden
设置默认的ELF镜像中符号的可见性为隐藏。使用这个特性能够很是充分的提升链接和加 载共享库的性能,生成更加优化的代码,提供近乎完美的API输出和防止符号碰撞。咱们强烈建议你在编译任何共享库的时候使用该选项。参见 -fvisibility-inlines-hidden 选项。

硬件体系结构相关选项[仅仅针对x86与x86_64]:

-march=cpu-type
为特定的cpu-type编译二进制代码(不能在更低级别的cpu上运行)。Intel能够 用:pentium2, pentium3(=pentium3m), pentium4(=pentium4m), pentium-m, prescott, nocona, core2(GCC-4.3新增) 。AMD能够用:k6-2(=k6-3), athlon(=athlon-tbird), athlon-xp(=athlon-mp), k8(=opteron=athlon64=athlon-fx)
-mfpmath=sse
P3和athlon-xp级别及以上的cpu支持"sse"标量浮点指令。仅建议在P4和K8以上级 别的处理器上使用该选项。
-malign-double
将double, long double, long long对齐于双字节边界上;有助于生成更高速的代码,可是程序的尺寸会变大,而且不能与未使用该选项编译的程序一块儿工做。
-m128bit-long-double
指定long double为128位,pentium以上的cpu更喜欢这种标准,而且符合x86-64的ABI标准,可是却不附合i386的ABI标准。
-mregparm=N
指定用于传递整数参数的寄存器数目(默认不使用寄存器)。0<=N<=3 ;注意:当N>0时你必须使用同一参数从新构建全部的模块,包括全部的库。
-msseregparm
使用SSE寄存器传递float和double参数和返回值。注意:当你使用了这个选项之后,你必须 使用同一参数从新构建全部的模块,包括全部的库。
-mmmx
-msse
-msse2
-msse3
-m3dnow
-mssse3(没写错!GCC-4.3 新增)
-msse4.1(GCC-4.3新增)
-msse4.2(GCC-4.3新增)
-msse4(含4.1和 4.2,GCC-4.3新增)
是否使用相应的扩展指令集以及内置函数,按照本身的cpu选择吧!
-maccumulate-outgoing-args
指定在函数引导段中计算输出参数所需最大空间,这在大部分现代cpu中 是较快的方法;缺点是会明显增长二进制文件尺寸。
-mthreads
支持Mingw32的线程安全异常处理。对于依赖于线程安全异常处理的程序,必须启用这个选项。使用这个选 项时会定义"-D_MT",它将包含使用选项"-lmingwthrd"链接的一个特殊的线程辅助库,用于为每一个线程清理异常处理数据。
-minline-all-stringops
默认时GCC只将肯定目的地会被对齐在至少4字节边界的字符串操做内联进程序代 码。该选项启用更多的内联而且增长二进制文件的体积,可是能够提高依赖于高速 memcpy, strlen, memset 操做的程序的性能。
-minline-stringops-dynamically
GCC-4.3新增。对未知尺寸字符串的小块操做使用内联代 码,而对大块操做仍然调用库函数,这是比"-minline-all-stringops"更聪明的策略。决定策略的算法能够通 过"-mstringop-strategy"控制。
-momit-leaf-frame-pointer
不为叶子函数在寄存器中保存栈指针,这样能够节省寄存器,可是将会使调试 变的困难。注意:不要与 -fomit-frame-pointer 同时使用,由于会形成代码效率低下。
-m64
生成专门运行于64位环境的代码,不能运行于32位环境,仅用于x86_64[含EMT64]环境。
-mcmodel=small
[默认值]程序和它的符号必须位于2GB如下的地址空间。指针仍然是64位。程序能够静态链接也 能够动态链接。仅用于x86_64[含EMT64]环境。
-mcmodel=kernel
内核运行于2GB地址空间以外。在编译linux内核时必须使用该选项!仅用于 x86_64[含EMT64]环境。
-mcmodel=medium
程序必须位于2GB如下的地址空间,可是它的符号能够位于任何地址空间。程序能够静态链接也可 以动态链接。注意:共享库不能使用这个选项编译!仅用于x86_64[含EMT64]环境。

其它优化选项:

-fforce-addr
必须将地址复制到寄存器中才能对他们进行运算。因为所需地址一般在前面已经加载到寄存器中了,因此这 个选项能够改进代码。
-finline-limit=n
对伪指令数超过n的函数,编译程序将不进行内联展开,默认为600。增大此值将增长编译时间 和编译内存用量而且生成的二进制文件体积也会变大,此值不宜太大。
-fmerge-all-constants
试图将跨编译单元的全部常量值和数组合并在一个副本中。可是标准C/C++要求每 个变量都必须有不一样的存储位置,因此该选项可能会致使某些不兼容的行为。
-fgcse-sm
在全局公共子表达式消除以后运行存储移动,以试图将存储移出循环。gcc-3.4中曾属于"-O2"级别的 选项。
-fgcse-las
在全局公共子表达式消除以后消除多余的在存储到同一存储区域以后的加载操做。gcc-3.4中曾属 于"-O2"级别的选项。
-floop-optimize
已废除(GCC-4.1曾包含在"-O1"中)。
-floop-optimize2
使用改进版本的循环优化器代替原来"-floop-optimize"。该优化器将使用不一样 的选项(-funroll-loops, -fpeel-loops, -funswitch-loops, -ftree-loop-im)分别控制循环优化的不一样方面。目前这个新版本的优化器尚在开发中,而且生成的代码质量并不比之前的版本高。已废除,仅存在 于GCC-4.1以前的版本中。
-funsafe-loop-optimizations
假定循环不会溢出,而且循环的退出条件不是无穷。这将能够在一个比较 广的范围内进行循环优化,即便优化器本身也不能判定这样作是否正确。
-fsched-spec-load
容许一些装载指令执行一些投机性的动做。
-ftree-loop-linear
在trees上进行线型循环转换。它可以改进缓冲性能而且容许进行更进一步的循环优化。
-fivopts
在trees上执行概括变量优化。
-ftree-vectorize
在trees上执行循环向量化。
-ftracer
执行尾部复制以扩大超级块的尺寸,它简化了函数控制流,从而容许其它的优化措施作的更好。听说挺有效。
-funroll-loops
仅对循环次数可以在编译时或运行时肯定的循环进行展开,生成的代码尺寸将变大,执行速度可能变快 也可能变慢。
-fprefetch-loop-arrays
生成数组预读取指令,对于使用巨大数组的程序能够加快代码执行速度,适合数据库 相关的大型软件等。具体效果如何取决于代码。
-fweb
创建常用的缓存器网络,提供更佳的缓存器使用率。gcc-3.4中曾属于"-O3"级别的选项。
-ffast-math
违反IEEE/ANSI标准以提升浮点数计算速度,是个危险的选项,仅在编译不须要严格遵照IEEE规 范且浮点计算密集的程序考虑采用。
-fsingle-precision-constant
将浮点常量做为单精度常量对待,而不是隐式地将其转换为双精度。
-fbranch-probabilities
在使用 -fprofile-arcs 选项编译程序并执行它来建立包含每一个代码块执行次数的文件以后,程序能够利用这一选项再次编译,文件中所产生的信息将被用来优化那些常常发生的分支代码。 若是没有这些信息,gcc将猜想那一分支可能常常发生并进行优化。这类优化信息将会存放在一个以源文件为名字的并以".da"为后缀的文件中。
-frename-registers
试图驱除代码中的假依赖关系,这个选项对具备大量寄存器的机器颇有效。gcc-3.4中 曾属于"-O3"级别的选项。
-fbranch-target-load-optimize
-fbranch-target-load-optimize2
在 执行序启动以及结尾以前执行分支目标缓存器加载最佳化。
-fstack-protector
在关键函数的堆栈中设置保护值。在返回地址和返回值以前,都将验证这个保护值。若是出现了 缓冲区溢出,保护值再也不匹配,程序就会退出。程序每次运行,保护值都是随机的,所以不会被远程猜出。
-fstack-protector-all
同上,可是在全部函数的堆栈中设置保护值。
--param max-gcse-memory=xxM
执行GCSE优化使用的最大内存量(xxM),过小将使该优化没法进 行,默认为50M。
--param max-gcse-passes=n
执行GCSE优化的最大迭代次数,默认为 1。

传递给汇编器的选项:

-Wa,options
options是一个或多个由逗号分隔的能够传递给汇编器的选项列表。其中的每个都可做为命令行选项 传递给汇编器。
-Wa,--strip-local-absolute
从输出符号表中移除局部绝对符号。
-Wa,-R
合并数据段和正文段,由于没必要在数据段和代码段之间转移,因此它可能会产生更短的地址移动。
-Wa,--64
设置字长为64bit,仅用于x86_64,而且仅对ELF格式的目标文件有效。此外,还须要使 用"--enable-64-bit-bfd"选项编译的BFD支持。
-Wa,-march=CPU
按照特定的CPU进行优化:pentiumiii, pentium4, prescott, nocona, core, core2; athlon, sledgehammer, opteron, k8 。

仅可用于 CFLAGS 的选项:

-fhosted
按宿主环境编译,其中须要有完整的标准库,入口必须是main()函数且具备int型的返回值。内核之外几乎 全部的程序都是如此。该选项隐含设置了 -fbuiltin,且与 -fno-freestanding 等价。
-ffreestanding
按独立环境编译,该环境能够没有标准库,且对main()函数没有要求。最典型的例子就是操做系 统内核。该选项隐含设置了 -fno-builtin,且与 -fno-hosted 等价。

仅可用于 CXXFLAGS 的选项:

-fno-enforce-eh-specs
C++标准要求强制检查异常违例,可是该选项能够关闭违例检查,从而减少生成代码 的体积。该选项相似于定义了"NDEBUG"宏。
-fno-rtti
若是没有使用'dynamic_cast'和'typeid',能够使用这个选项禁止为包含虚方法的类生成 运行时表示代码,从而节约空间。此选项对于异常处理无效(仍然按需生成rtti代码)。
-ftemplate-depth-n
将最大模版实例化深度设为'n',符合标准的程序不能超过17,默认值为500。
-fno-optional-diags
禁止输出诊断消息,C++标准并不须要这些消息。
-fno-threadsafe-statics
GCC自动在访问C++局部静态变量的代码上加锁,以保证线程安全。若是你不 须要线程安全,能够使用这个选项。
-fvisibility-inlines-hidden
默认隐藏全部内联函数,从而减少导出符号表的大小,既能缩减文件的大 小,还能提升运行性能,咱们强烈建议你在编译任何共享库的时候使用该选项。参见 -fvisibility=hidden 选项。

LDFLAGS

LDFLAGS 是传递给链接器的选项。这是一个常被忽视的变量,事实上它对优化的影响也是很明显的。

[提示]如下选项是在完整的阅读了ld-2.18文档以后挑选出来的选项。 http://blog.chinaunix.net/u1/41220/showart_354602.html 有2.14版本的中文手册。

-s
删除可执行程序中的全部符号表和全部重定位信息。其结果与运行命令 strip 所达到的效果相同,这个选项是比较安全的。
-Wl,options
options是由一个或多个逗号分隔的传递给连接器的选项列表。其中的每个选项均会做为命令行选项 提供给连接器。
-Wl,-On
当n>0时将会优化输出,可是会明显增长链接操做的时间,这个选项是比较安全的。
-Wl,--exclude-libs=ALL
不自动导出库中的符号,也就是默认将库中的符号隐藏。
-Wl,-m<emulation>
仿真<emulation>链接器,当前ld全部可用的仿真能够 经过"ld -V"命令获取。默认值取决于ld的编译时配置。
-Wl,--sort-common
把全局公共符号按照大小排序后放到适当的输出节,以防止符号间由于排布限制而出现间隙。
-Wl,-x
删除全部的本地符号。
-Wl,-X
删除全部的临时本地符号。对于大多数目标平台,就是全部的名字以'L'开头的本地符号。
-Wl,-zcomberloc
组合多个重定位节并从新排布它们,以便让动态符号能够被缓存。
-Wl,--enable-new-dtags
在ELF中建立新式的"dynamic tags",但在老式的ELF系统上没法识别。
-Wl,--as-needed
移除没必要要的符号引用,仅在实际须要的时候才链接,能够生成更高效的代码。
-Wl,--no-define-common
限制对普通符号的地址分配。该选项容许那些从共享库中引用的普通符号只在主程序 中被分配地址。这会消除在共享库中的无用的副本的空间,同时也防止了在有多个指定了搜索路径的动态模块在进行运行时符号解析时引发的混乱。
-Wl,--hash-style=gnu
使用gnu风格的符号散列表格式。它的动态连接性能比传统的sysv风格(默认)有 较大提高,可是它生成的可执行程序和库与旧的Glibc以及动态连接器不兼容。

最后说两个与优化无关的系统环境变量,由于会影响GCC编译程序的方式,下面两个是咱中国人比较关心的:

LANG
指定编译程序使用的字符集,可用于建立宽字符文件、串文字、注释;默认为英文。[目前只支持日文"C-JIS,C- SJIS,C-EUCJP",不支持中文]
LC_ALL
指定多字节字符的字符分类,主要用于肯定字符串的字符边界以及编译程序使用何种语言发出诊断消息;默认设置与 LANG相同。中文相关的几项:"zh_CN.GB2312 , zh_CN.GB18030 , zh_CN.GBK , zh_CN.UTF-8 , zh_TW.BIG5"。

 

 
  http://www.javashuo.com/article/p-gwuzmvle-bm.html
 
 
GCG  -o选项用来指定输出文件,它的用法为:

[infile] -o [outfile]

[infile] 表示输入文件(也即要处理的文件),它能够是源文件,也能够是汇编文件或者是目标文件;[outfile] 表示输出文件(也即处理的结果),它能够是预处理文件、目标文件、可执行文件等。

[infile] 和 [outfile] 能够是一个文件,也能够是一组文件:
  • 若是 [infile] 是一组文件,那么就表示有多个输入文件;
  • 若是 [outfile] 是一组文件,那么就表示有多个输出文件。

若是不使用 -o 选项,那么将采用默认的输出文件,例如,把可执行文件做为输出文件,它的名字默认为 a.out。

GCC -o选项使用举例

1) 将源文件做为输入文件,将可执行文件做为输出文件,也即完整地编译整个程序:

$ gcc main.c func.c -o app.out

将 main.c 和 func.c 两个源文件编译成一个可执行文件,其名字为 app.out。若是不使用 -o 选项,那么将生成名字为 a.out 的可执行文件。

2) 将源文件做为输入文件,将目标文件做为输出文件,也即只编译不连接:

$ gcc -c main.c -o a.o

将源文件 main.c 编译为目标文件 a.o。若是不使用 -o 选项,那么将生成名为 main.o 的目标文件。

3) 将源文件做为输入文件,将预处理文件做为输出文件,也即只进行预处理操做:

$ gcc -E main.c -o demo.i

对源文件 main.c 进行预处理操做,并将结果放在 demo.i 文件中。若是不使用 -o 选项,那么将生成名为 main.i 的预处理文件。

4) 将目标文件做为输入文件,将可执行文件做为输出文件:

$ gcc -c func.c main.c
$ gcc func.o main.o -o app.out

第一条命令只编译不连接,将生成 func.o 和 main.o 两个目标文件。第二条命令将生成的两个目标文件生成最终的可执行文件 app.out。若是不使用 -o 选项,那么将生成名字为 a.out 的可执行文件。
 
 
-g为了调试用的
加个-g 是为了 gdb 用,否则 gdb用不到

-o output_filename,肯定输出文件的名称为output_filename,同时这个名称不能和源文件同名。若是不给出这个选项,gcc就给出预设的可执行文件a.out。
通常语法:
gcc filename.c -o filename
上面的意思是若是你不打 -o filename 那么默认就是输出filemame.out.这个-o就是用来控制输出文件的。

-c 只编译不连接
 
追问
-c 只编译不连接 
打上-c就是不产生可执行文件吗?
追答
产生.o文件,就是obj文件,不产生执行文件

 

 
 
 

经常使用选项

 

-E:只进行预处理,不编译
-S:只编译,不汇编
-c:只编译、汇编,不连接
-g:包含调试信息
-I:指定include包含文件的搜索目录
-o:输出成指定文件名

高级选项

-v:详细输出编译过程当中所采用的每个选项
-C:预处理时保留注释信息
-ggdb:在可执行文件中包含可供GDB使用的调试信息
-fverbose-asm:在编译成汇编语言时,把C变量的名称做为汇编语言中的注释
-save-temps:自动输出预处理文件、汇编文件、对象文件,编译正常进行
-fsyntax-only:只测试源文件语法是否正确,不会进行任何编译操做
-ffreestanding:编译成独立程序,而非宿主程序

语言标准

-ansi:ANSI标准
-std=c99:C99标准
-std=gnu89:ISO/IEC 9899:1990 以及GNU扩充
-std=gnu99:ISO/IEC 9899:1999 以及GNU扩充
-trigraphs:支持ISO C三字符组

出错提示

-w:忽略全部警告
-Werror:不区分警告和错误,遇到任何警告都中止编译
-Wall:开启大部分警告提示
-Wshadow:某语句块做用域变量与更大做用域的另外一变量同名时发出警告(此警告未包含在-Wall选项中,需单独开启)
-Wextra:对全部合法但值得怀疑的表达式发出警告

优化选项

-O0:关闭全部优化选项
-O1:第一级别优化,使用此选项可以使可执行文件更小、运行更快,并不会增长太多编译时间,能够简写为-O
-O2:第二级别优化,采用了几乎全部的优化技术,使用此选项会延长编译时间
-O3:第三级别优化,在-O2的基础上增长了产生inline函数、使用寄存器等优化技术
-Os:此选项相似于-O2,做用是优化所占用的空间,但不会进行性能优化,经常使用于生成最终版本


 

 

 

 

GNU CC(简称为Gcc)是GNU项目中符合ANSI C标准的编译系统,可以编译用C、C++和Object C等语言编写的程序。Gcc不只功能强大,并且能够编译如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多种语言,并且Gcc又是一个交叉平台编译器,它可以在当前CPU平台上为多种不一样体系结构的硬件平台开发软件,所以尤为适合在嵌入式领域的开发编译。本章中的示例,除非特别注明,不然均采用Gcc版本为4.0.0。

 

 

GCC入门基础

表3.6 Gcc所支持后缀名解释

后 缀 名

所对应的语言

后 缀 名

所对应的语言

.c

C原始程序

.s/.S

汇编语言原始程序

.C/.cc/.cxx

C++原始程序

.h

预处理文件(头文件)

.m

Objective-C原始程序

.o

目标文件

.i

已通过预处理的C原始程序

.a/.so

编译后的库文件

.ii

已通过预处理的C++原始程序

   

如本章开头提到的,Gcc的编译流程分为了四个步骤,分别为:

· 预处理(Pre-Processing)

· 编译(Compiling)

· 汇编(Assembling)

· 连接(Linking)

下面就具体来查看一下Gcc是如何完成四个步骤的。

首先,有如下hello.c源代码

#include<stdio.h>

int main()

{

printf("Hello! This is our embedded world!n");

return 0;

}

(1)预处理阶段

在该阶段,编译器将上述代码中的stdio.h编译进来,而且用户能够使用Gcc的选项”-E”进行查看,该选项的做用是让Gcc在预处理结束后中止编译过程。

 

注意

Gcc指令的通常格式为:Gcc [选项] 要编译的文件 [选项] [目标文件]

其中,目标文件可缺省,Gcc默认生成可执行的文件,命为:编译文件.out

 

[root@localhost Gcc]# gcc -E hello.c  -o hello.i

 

在此处,选项”-o”是指目标文件,由表3.6可知,”.i”文件为已通过预处理的C原始程序。如下列出了hello.i文件的部份内容:

 

typedef int (*__gconv_trans_fct) (struct __gconv_step *,

struct __gconv_step_data *, void *,

__const unsigned char *,

__const unsigned char **,

__const unsigned char *, unsigned char **,

size_t *);

# 2 "hello.c" 2

int main()

{

printf("Hello! This is our embedded world!n");

return 0;

}

 

因而可知,Gcc确实进行了预处理,它把”stdio.h”的内容插入到hello.i文件中。

(2)编译阶段

接下来进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以肯定代码的实际要作的工做,在检查无误后,Gcc把代码翻译成汇编语言。用户能够使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。

 

[root@localhost Gcc]# Gcc -S hello.i -o hello.s

 

如下列出了hello.s的内容,可见Gcc已经将其转化为汇编了,感兴趣的读者能够分析一下这一行简单的C语言小程序是如何用汇编代码实现的。

 

.file "hello.c"

.section .rodata

.align 4

.LC0:

.string"Hello! This is our embedded world!"

.text

.globl main

.type main, @function

main:

pushl �p

movl %esp, �p

subl $8, %esp

andl $-16, %esp

movl $0, �x

addl $15, �x

addl $15, �x

shrl $4, �x

sall $4, �x

subl �x, %esp

subl $12, %esp

pushl $.LC0

call puts

addl $16, %esp

movl $0, �x

leave

ret

.size main, .-main

.ident "GCC: (GNU) 4.0.0 20050519 (Red Hat 4.0.0-8)"

.section .note.GNU-stack,"",@progbits

 

(3)汇编阶段

汇编阶段是把编译阶段生成的”.s”文件转成目标文件,读者在此可以使用选项”-c”就可看到汇编代码已转化为”.o”的二进制目标代码了。以下所示:

 

[root@localhost Gcc]# gcc -c hello.s -o hello.o

 

(4)连接阶段

在成功编译以后,就进入了连接阶段。在这里涉及到一个重要的概念:函数库。

读者能够从新查看这个小程序,在这个程序中并无定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被作到名为libc.so.6的库文件中去了,在没有特别指定时,Gcc会到系统默认的搜索路径”/usr/lib”下进行查找,也就是连接到libc.so.6库函数中去,这样就能实现函数”printf”了,而这也就是连接的做用。

函数库通常分为静态库和动态库两种。静态库是指编译连接时,把库文件的代码所有加入到可执行文件中,所以生成的文件比较大,但在运行时也就再也不须要库文件了。其后缀名通常为”.a”。动态库与之相反,在编译连接时并无把库文件的代码加入到可执行文件中,而是在程序执行时由运行时连接文件加载库,这样能够节省系统的开销。动态库通常后缀名为”.so”,如前面所述的libc.so.6就是动态库。Gcc在编译时默认使用动态库。

完成了连接以后,Gcc就能够生成可执行文件,以下所示。

 

[root@localhost Gcc]# gcc hello.o -o hello

 

运行该可执行文件,出现正确的结果以下。

 

[root@localhost Gcc]# ./hello

Hello! This is our embedded world!

Gcc编译选项分析

Gcc有超过100个的可用选项,主要包括整体选项、告警和出错选项、优化选项和体系结构相关选项。如下对每一类中最经常使用的选项进行讲解。

(1)整体选项

Gcc的总结选项如表3.7所示,不少在前面的示例中已经有所涉及。

表3.7 Gcc整体选项列表

后缀名

所对应的语言

-c

只是编译不连接,生成目标文件“.o”

-S

只是编译不汇编,生成汇编代码

-E

只进行预编译,不作其余处理

-g

在可执行程序中包含标准调试信息

-o file

把输出文件输出到file里

-v

打印出编译器内部编译各过程的命令行信息和编译器的版本

-I dir

在头文件的搜索路径列表中添加dir目录

-L dir

在库文件的搜索路径列表中添加dir目录

-static

连接静态库

-llibrary

链接名为library的库文件

 

对于“-c”、“-E”、“-o”、“-S”选项在前一小节中已经讲解了其使用方法,在此主要讲解另外两个很是经常使用的库依赖选项“-I dir”和“-L dir”。

· “-I dir”

正如上表中所述,“-I dir”选项能够在头文件的搜索路径列表中添加dir目录。因为Linux中头文件都默认放到了“/usr/include/”目录下,所以,当用户但愿添加放置在其余位置的头文件时,就能够经过“-I dir”选项来指定,这样,Gcc就会到相应的位置查找对应的目录。

好比在“/root/workplace/Gcc”下有两个文件:

 

 

#include<my.h>

int main()

{

printf(“Hello!!n”);

return 0;

}

 

#include<stdio.h>

 

这样,就可在Gcc命令行中加入“-I”选项:

 

[root@localhost Gcc] Gcc hello1.c –I /root/workplace/Gcc/ -o hello1

 

这样,Gcc就可以执行出正确结果。

 

小知识

在include语句中,“<>”表示在标准路径中搜索头文件,““””表示在本目录中搜索。故在上例中,可把hello1.c的“#include<my.h>”改成“#include “my.h””,就不须要加上“-I”选项了。

 

· “-L dir”

选项“-L dir”的功能与“-I dir”相似,可以在库文件的搜索路径列表中添加dir目录。例若有程序hello_sq.c须要用到目录“/root/workplace/Gcc/lib”下的一个动态库libsunq.so,则只需键入以下命令便可:

 

[root@localhost Gcc] Gcc hello_sq.c –L /root/workplace/Gcc/lib –lsunq –o hello_sq

 

须要注意的是,“-I dir”和“-L dir”都只是指定了路径,而没有指定文件,所以不能在路径中包含文件名。

另外值得详细解释一下的是“-l”选项,它指示Gcc去链接库文件libsunq.so。因为在Linux下的库文件命名时有一个规定:必须以lib三个字母开头。所以在用-l选项指定连接的库文件名时能够省去lib三个字母。也就是说Gcc在对”-lsunq”进行处理时,会自动去连接名为libsunq.so的文件。

(2)告警和出错选项

Gcc的告警和出错选项如表3.8所示。

表3.8 Gcc整体选项列表

选项

含义

-ansi

支持符合ANSI标准的C程序

-pedantic

容许发出ANSI C标准所列的所有警告信息

选项

含义

-pedantic-error

容许发出ANSI C标准所列的所有错误信息

-w

关闭全部告警

-Wall

容许发出Gcc提供的全部有用的报警信息

-werror

把全部的告警信息转化为错误信息,并在告警发生时终止编译过程

 

下面结合实例对这几个告警和出错选项进行简单的讲解。

若有如下程序段:

 

#include<stdio.h>

 

void main()

{

long long tmp = 1;

printf(“This is a bad code!n”);

return 0;

}

 

这是一个很糟糕的程序,读者能够考虑一下有哪些问题?

· “-ansi”

该选项强制Gcc生成标准语法所要求的告警信息,尽管这还并不能保证全部没有警告的程序都是符合ANSI C标准的。运行结果以下所示:

 

[root@localhost Gcc]# Gcc –ansi warning.c –o warning

warning.c: 在函数“main”中:

warning.c:7 警告:在无返回值的函数中,“return”带返回值

warning.c:4 警告:“main”的返回类型不是“int”

 

能够看出,该选项并无发现”long long”这个无效数据类型的错误。

· “-pedantic”

容许发出ANSI C标准所列的所有警告信息,一样也保证全部没有警告的程序都是符合ANSI C标准的。其运行结果以下所示:

 

[root@localhost Gcc]# Gcc –pedantic warning.c –o warning

warning.c: 在函数“main”中:

warning.c:5 警告:ISO C90不支持“long long”

warning.c:7 警告:在无返回值的函数中,“return”带返回值

warning.c:4 警告:“main”的返回类型不是“int”

 

能够看出,使用该选项查看出了”long long”这个无效数据类型的错误。

· “-Wall”

容许发出Gcc可以提供的全部有用的报警信息。该选项的运行结果以下所示:

[root@localhost Gcc]# Gcc –Wall warning.c –o warning

warning.c:4 警告:“main”的返回类型不是“int”

warning.c: 在函数”main”中:

warning.c:7 警告:在无返回值的函数中,”return”带返回值

warning.c:5 警告:未使用的变量“tmp”

 

使用“-Wall”选项找出了未使用的变量tmp,但它并无找出无效数据类型的错误。

另外,Gcc还能够利用选项对单独的常见错误分别指定警告,有关具体选项的含义感兴趣的读者能够查看Gcc手册进行学习。

(3)优化选项

Gcc能够对代码进行优化,它经过编译选项“-On”来控制优化代码的生成,其中n是一个表明优化级别的整数。对于不一样版本的Gcc来说,n的取值范围及其对应的优化效果可能并不彻底相同,比较典型的范围是从0变化到2或3。

不一样的优化级别对应不一样的优化处理工做。如使用优化选项“-O”主要进行线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。使用优化选项“-O2”除了完成全部“-O1”级别的优化以外,同时还要进行一些额外的调整工做,如处理器指令调度等。选项“-O3”则还包括循环展开和其余一些与处理器特性相关的优化工做。

虽然优化选项能够加速代码的运行速度,但对于调试而言将是一个很大的挑战。由于代码在通过优化以后,原先在源程序中声明和使用的变量极可能再也不使用,控制流也可能会忽然跳转到意外的地方,循环语句也有可能由于循环展开而变获得处都有,全部这些对调试来说都将是一场噩梦。因此笔者建议在调试的时候最好不使用任何优化选项,只有当程序在最终发行的时候才考虑对其进行优化。

(4)体系结构相关选项

Gcc的体系结构相关选项如表3.9所示。

表3.9Gcc体系结构相关选项列表

选项

含义

-mcpu=type

针对不一样的CPU使用相应的CPU指令。可选择的type有i38六、i48六、pentium及i686等

-mieee-fp

使用IEEE标准进行浮点数的比较

-mno-ieee-fp

不使用IEEE标准进行浮点数的比较

-msoft-float

输出包含浮点库调用的目标代码

-mshort

把int类型做为16位处理,至关于short int

-mrtd

强行将函数参数个数固定的函数用ret NUM返回,节省调用函数的一条指令

 

这些体系结构相关选项在嵌入式的设计中会有较多的应用,读者需根据不一样体系结构将对应的选项进行组合处理。在本书后面涉及到具体实例会有针对性的讲解。

Gdb调试器

调试是全部程序员都会面临的问题。如何提升程序员的调试效率,更好更快地定位程序中的问题从而加快程序开发的进度,是你们共同面对的。就如读者熟知的Windows下的一些调试工具,如VC自带的如设置断点、单步跟踪等,都受到了广大用户的赞扬。那么,在Linux下有什么很好的调试工具呢?

本文所介绍的Gdb调试器是一款GNU开发组织并发布的UNIX/Linux下的程序调试工具。虽然,它没有图形化的友好界面,可是它强大的功能也足以与微软的VC工具等媲美。下面就请跟随笔者一步步学习Gdb调试器。

Gdb使用流程

首先,笔者给出了一个短小的程序,由此带领读者熟悉一下Gdb的使用流程。强烈建议读者可以实际动手操做。

首先,打开Linux下的编辑器Vi或者Emacs,编辑以下代码。(因为为了更好地熟悉Gdb的操做,笔者在此使用Vi编辑,但愿读者可以参见3.3节中对Vi的介绍,并熟练使用Vi)。

 

 

#include <stdio.h>

int sum(int m);

int main()

{

int i,n=0;

sum(50);

for(i=1; i<=50; i++)

{

n += i;

}

printf("The sum of 1-50 is %d n", n );

 

}

int sum(int m)

{

int i,n=0;

for(i=1; i<=m;i++)

n += i;

printf("The sum of 1-m is %dn", n);

}

 

在保存退出后首先使用Gcc对test.c进行编译,注意必定要加上选项”-g”,这样编译出的可执行代码中才包含调试信息,不然以后Gdb没法载入该可执行文件。

 

[root@localhost Gdb]# gcc -g test.c -o test

 

虽然这段程序没有错误,但调试彻底正确的程序能够更加了解Gdb的使用流程。接下来就启动Gdb进行调试。注意,Gdb进行调试的是可执行文件,而不是如”.c”的源代码,所以,须要先经过Gcc编译生成可执行文件才能用Gdb进行调试。

 

[root@localhost Gdb]# gdb test

GNU Gdb Red Hat Linux (6.3.0.0-1.21rh)

Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB. Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb)

 

能够看出,在Gdb的启动画面中指出了Gdb的版本号、使用的库文件等信息,接下来就进入了由“(gdb)”开头的命令行界面了。

(1)查看文件

在Gdb中键入”l”(list)就能够查看所载入的文件,以下所示:

 

 

注意

在Gdb的命令中均可使用缩略形式的命令,如“l”代便“list”,“b”表明“breakpoint”,“p”表明“print”等,读者也可以使用“help”命令查看帮助信息。

 

(Gdb) l

1 #include <stdio.h>

2 int sum(int m);

3 int main()

4 {

5 int i,n=0;

6 sum(50);

7 for(i=1; i<=50; i++)

8 {

9 n += i;

10 }

(Gdb) l

11 printf("The sum of 1~50 is %d n", n );

12

13 }

14 int sum(int m)

15 {

16 int i,n=0;

17 for(i=1; i<=m;i++)

18 n += i;

19 printf("The sum of 1~m is = %dn", n);

20 }

 

能够看出,Gdb列出的源代码中明确地给出了对应的行号,这样就能够大大地方便代码的定位。

(2)设置断点

设置断点是调试程序中是一个很是重要的手段,它能够使程序到必定位置暂停它的运行。所以,程序员在该位置处能够方便地查看变量的值、堆栈状况等,从而找出代码的症结所在

在Gdb中设置断点很是简单,只需在”b”后加入对应的行号便可(这是最经常使用的方式,另外还有其余方式设置断点)。以下所示:

 

(Gdb) b 6

Breakpoint 1 at 0x804846d: file test.c, line 6.

 

要注意的是,在Gdb中利用行号设置断点是指代码运行到对应行以前将其中止,如上例中,代码运行到第五行以前暂停(并无运行第五行)。

(3)查看断点状况

在设置完断点以后,用户能够键入”info b”来查看设置断点状况,在Gdb中能够设置多个断点。

 

(Gdb) info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x0804846d in main at test.c:6

 

(4)运行代码

接下来就可运行代码了,Gdb默认从首行开始运行代码,可键入”r”(run)便可(若想从程序中指定行开始运行,可在r后面加上行号)。

 

(Gdb) r

Starting program: /root/workplace/Gdb/test

Reading symbols from shared object read from target memory...done.

Loaded system supplied DSO at 0x5fb000

 

Breakpoint 1, main () at test.c:6

6 sum(50);

 

能够看到,程序运行到断点处就中止了。

(5)查看变量值

在程序中止运行以后,程序员所要作的工做是查看断点处的相关变量值。在Gdb中只需键入”p”+变量值便可,以下所示:

 

(Gdb) p n

$1 = 0

(Gdb) p i

$2 = 134518440

 

在此处,为何变量”i”的值为如此奇怪的一个数字呢?缘由就在于程序是在断点设置的对应行以前中止的,那么在此时,并无把”i”的数值赋为零,而只是一个随机的数字。但变量”n”是在第四行赋值的,故在此时已经为零。

 

小技巧

Gdb在显示变量值时都会在对应值以前加上”$N”标记,它是当前变量值的引用标记,因此之后若想再次引用此变量就能够直接写做”$N”,而无需写冗长的变量名。

 

(6)单步运行

单步运行能够使用命令”n”(next)或”s”(step),它们之间的区别在于:如有函数调用的时候,”s”会进入该函数而”n”不会进入该函数。所以,”s”就相似于VC等工具中的”step in”,”n”相似与VC等工具中的”step over”。它们的使用以下所示:

 

(Gdb) n

The sum of 1-m is 1275

7 for(i=1; i<=50; i++)

(Gdb) s

sum (m=50) at test.c:16

16 int i,n=0;

 

可见,使用”n”后,程序显示函数sum的运行结果并向下执行,而使用”s”后则进入到sum函数之中单步运行。

(7)恢复程序运行

在查看完所需变量及堆栈状况后,就能够使用命令”c”(continue)恢复程序的正常运行了。这时,它会把剩余还未执行的程序执行完,并显示剩余程序中的执行结果。如下是以前使用”n”命令恢复后的执行结果:

 

(Gdb) c

Continuing.

The sum of 1-50 is :1275

 

Program exited with code 031.

 

能够看出,程序在运行完后退出,以后程序处于“中止状态”。

 

小知识

在Gdb中,程序的运行状态有“运行”、“暂停”和“中止”三种,其中“暂停”状态为程序遇到了断点或观察点之类的,程序暂时中止运行,而此时函数的地址、函数参数、函数内的局部变量都会被压入“栈”(Stack)中。故在这种状态下能够查看函数的变量值等各类属性。但在函数处于“中止”状态以后,“栈”就会自动撤销,它也就没法查看各类信息了。

Gdb基本命令

Gdb的命令能够经过查看help进行查找,因为Gdb的命令不少,所以Gdb的help将其分红了不少种类(class),用户能够经过进一步查看相关class找到相应命令。以下所示:

 

(gdb) help

List of classes of commands:

 

aliases -- Aliases of other commands

breakpoints -- Making program stop at certain points

data -- Examining data

files -- Specifying and examining files

internals -- Maintenance commands

Type "help" followed by a class name for a list of commands in that class.

Type "help" followed by command name for full documentation.

Command name abbreViations are allowed if unambiguous.

 

上述列出了Gdb各个分类的命令,注意底部的加粗部分说明其为分类命令。接下来能够具体查找各分类种的命令。以下所示:

 

(gdb) help data

Examining data.

 

List of commands:

 

call -- Call a function in the program

delete display -- Cancel some expressions to be displayed when program stops

delete mem -- Delete memory region

disable display -- Disable some expressions to be displayed when program stops

Type "help" followed by command name for full documentation.

Command name abbreViations are allowed if unambiguous.

 

至此,若用户想要查找call命令,就可键入“help call”。

 

(gdb) help call

Call a function in the program.

The argument is the function name and arguments, in the notation of the

current working language. The result is printed and saved in the value

history, if it is not void.

 

固然,若用户已知命令名,直接键入“help [command]”也是能够的。

Gdb中的命令主要分为如下几类:工做环境相关命令、设置断点与恢复命令、源代码查看命令、查看运行数据相关命令及修改运行参数命令。如下就分别对这几类的命令进行讲解。

1.工做环境相关命令

Gdb中不只能够调试所运行的程序,并且还能够对程序相关的工做环境进行相应的设定,甚至还能够使用shell中的命令进行相关的操做,其功能极其强大。表3.10所示列出了Gdb常见工做环境相关命令。

表3.10 Gdb工做环境相关命令

命 令 格 式

含义

set args运行时的参数

指定运行时参数,如:set args 2

show args

查看设置好的运行参数

path dir

设定程序的运行路径

show paths

查看程序的运行路径

set enVironment var [=value]

设置环境变量

show enVironment [var]

查看环境变量

cd dir

进入到dir目录,至关于shell中的cd命令

pwd

显示当前工做目录

shell command

运行shell的command命令

2.设置断点与恢复命令

Gdb中设置断点与恢复的常见命令如表3.11所示。

表3.11 Gdb设置断点与恢复相关命令

命 令 格 式

含义

bnfo b

查看所设断点

break 行号或函数名 <条件表达式>

设置断点

tbreak 行号或函数名 <条件表达式>

设置临时断点,到达后被自动删除

delete [断点号]

删除指定断点,其断点号为”info b”中的第一栏。若缺省断点号则删除全部断点

disable [断点号]]

中止指定断点,使用”info b”仍能查看此断点。同delete同样,省断点号则中止全部断点

enable [断点号]

激活指定断点,即激活被disable中止的断点

condition [断点号] <条件表达式>

修改对应断点的条件

ignore [断点号]<num>

在程序执行中,忽略对应断点num次

step

单步恢复程序运行,且进入函数调用

next

单步恢复程序运行,但不进入函数调用

finish

运行程序,直到当前函数完成返回

c

继续执行函数,直到函数结束或遇到新的断点

 

因为设置断点在Gdb的调试中很是重要,因此在此再着重讲解一下Gdb中设置断点的方法。

Gdb中设置断点有多种方式:其一是按行设置断点,设置方法在3.5.1节已经指出,在此就不重复了。另外还能够设置函数断点和条件断点,在此结合上一小节的代码,具体介绍后两种设置断点的方法。

① 函数断点

Gdb中按函数设置断点只需把函数名列在命令”b”以后,以下所示:

 

(gdb) b sum

Breakpoint 1 at 0x80484ba: file test.c, line 16.

(gdb) info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x080484ba in sum at test.c:16

 

要注意的是,此时的断点实际是在函数的定义处,也就是在16行处(注意第16行还未执行)。

② 条件断点

Gdb中设置条件断点的格式为:b 行数或函数名 if 表达式。具体实例以下所示:

 

(gdb) b 8 if i==10

Breakpoint 1 at 0x804848c: file test.c, line 8.

(gdb) info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x0804848c in main at test.c:8

stop only if i == 10

(gdb) r

Starting program: /home/yul/test

The sum of 1-m is 1275

 

Breakpoint 1, main () at test.c:9

9 n += i;

(gdb) p i

$1 = 10

 

能够看到,该例中在第8行(也就是运行完第7行的for循环)设置了一个“i==0”的条件断点,在程序运行以后能够看出,程序确实在i为10时暂停运行。

3.Gdb中源码查看相关命令

在Gdb中能够查看源码以方便其余操做,它的常见相关命令如表3.12所示:

表3.12 Gdb源码查看相关相关命令

命 令 格 式

含义

list <行号>|<函数名>

查看指定位置代码

file [文件名]

加载指定文件

forward-search 正则表达式

源代码前向搜索

reverse-search 正则表达式

源代码后向搜索

dir dir

中止路径名

show directories

显示定义了的源文件搜索路径

info line

显示加载到Gdb内存中的代码

4.Gdb中查看运行数据相关命令

Gdb中查看运行数据是指当程序处于“运行”或“暂停”状态时,能够查看的变量及表达式的信息,其常见命令如表3.13所示:

表3.13 Gdb查看运行数据相关命令

命 令 格 式

含义

print 表达式|变量

查看程序运行时对应表达式和变量的值

x <n/f/u>

查看内存变量内容。其中n为整数表示显示内存的长度,f表示显示的格式,u表示从当前地址日后请求显示的字节数

display 表达式

设定在单步运行或其余状况中,自动显示的对应表达式的内容

5.Gdb中修改运行参数相关命令

Gdb还能够修改运行时的参数,并使该变量按照用户当前输入的值继续运行。它的设置方法为:在单步执行的过程当中,键入命令“set 变量=设定值”。这样,在此以后,程序就会按照该设定的值运行了。下面,笔者结合上一节的代码将n的初始值设为4,其代码以下所示:

 

(Gdb) b 7

Breakpoint 5 at 0x804847a: file test.c, line 7.

(Gdb) r

Starting program: /home/yul/test

The sum of 1-m is 1275

 

Breakpoint 5, main () at test.c:7

7 for(i=1; i<=50; i++)

(Gdb) set n=4

(Gdb) c

Continuing.

The sum of 1-50 is 1279

 

Program exited with code 031.

 

能够看到,最后的运行结果确实比以前的值大了4。

 

 

Gdb的使用切记点:

· 在Gcc编译选项中必定要加入”-g”。

· 只有在代码处于“运行”或“暂停”状态时才能查看变量值。

· 设置断点后程序在指定行以前中止。

Make工程管理器

到此为止,读者已经了解了如何在Linux下使用编辑器编写代码,如何使用Gcc把代码编译成可执行文件,还学习了如何使用Gdb来调试程序,那么,全部的工做看似已经完成了,为何还须要Make这个工程管理器呢?

所谓工程管理器,顾名思义,是指管理较多的文件的。读者能够试想一下,有一个上百个文件的代码构成的项目,若是其中只有一个或少数几个文件进行了修改,按照以前所学的Gcc编译工具,就不得不把这全部的文件从新编译一遍,由于编译器并不知道哪些文件是最近更新的,而只知道须要包含这些文件才能把源代码编译成可执行文件,因而,程序员就不能再也不从新输入数目如此庞大的文件名以完成最后的编译工做。

可是,请读者仔细回想一下本书在3.1.2节中所阐述的编译过程,编译过程是分为编译、汇编、连接不一样阶段的,其中编译阶段仅检查语法错误以及函数与变量的声明是否正确声明了,在连接阶段则主要完成是函数连接和全局变量的连接。所以,那些没有改动的源代码根本不须要从新编译,而只要把它们从新连接进去就能够了。因此,人们就但愿有一个工程管理器可以自动识别更新了的文件代码,同时又不须要重复输入冗长的命令行,这样,Make工程管理器也就应运而生了。

实际上,Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它可以根据文件时间戳自动发现更新过的文件而减小编译的工做量,同时,它经过读入Makefile文件的内容来执行大量的编译工做。用户只需编写一次简单的编译语句就能够了。它大大提升了实际项目的工做效率,并且几乎全部Linux下的项目编程均会涉及到它,但愿读者可以认真学习本节内容。

Makefile基本结构

Makefile是Make读入的唯一配置文件,所以本节的内容实际就是讲述Makefile的编写规则。在一个Makefile中一般包含以下内容:

· 须要由make工具建立的目标体(target),一般是目标文件或可执行文件;

· 要建立的目标体所依赖的文件(dependency_file);

· 建立每一个目标体时须要运行的命令(command)。

它的格式为:

 

target: dependency_files

command

 

例如,有两个文件分别为hello.c和hello.h,建立的目标体为hello.o,执行的命令为gcc编译指令:gcc –c hello.c,那么,对应的Makefile就能够写为:

 

#The simplest example

hello.o: hello.c hello.h

gcc –c hello.c –o hello.o

 

接着就能够使用make了。使用make的格式为:make target,这样make就会自动读入Makefile(也能够是首字母小写makefile)并执行对应target的command语句,并会找到相应的依赖文件。以下所示:

 

[root@localhost makefile]# make hello.o

gcc –c hello.c –o hello.o

[root@localhost makefile]# ls

hello.c hello.h hello.o Makefile

 

能够看到,Makefile执行了“hello.o”对应的命令语句,并生成了“hello.o”目标体。

 

 

注意

在Makefile中的每个command前必须有“Tab”符,不然在运行make命令时会出错。

Makefile变量

上面示例的Makefile在实际中是几乎不存在的,由于它过于简单,仅包含两个文件和一个命令,在这种状况下彻底没必要要编写Makefile而只需在Shell中直接输入便可,在实际中使用的Makefile每每是包含不少的文件和命令的,这也是Makefile产生的缘由。下面就可给出稍微复杂一些的Makefile进行讲解:

 

sunq:kang.o yul.o

Gcc kang.o bar.o -o myprog

kang.o : kang.c kang.h head.h

Gcc –Wall –O -g –c kang.c -o kang.o

yul.o : bar.c head.h

Gcc - Wall –O -g –c yul.c -o yul.o

 

在这个Makefile中有三个目标体(target),分别为sunq、kang.o和yul.o,其中第一个目标体的依赖文件就是后两个目标体。若是用户使用命令“make sunq”,则make管理器就是找到sunq目标体开始执行。

这时,make会自动检查相关文件的时间戳。首先,在检查“kang.o”、“yul.o”和“sunq”三个文件的时间戳以前,它会向下查找那些把“kang.o”或“yul.o”作为目标文件的时间戳。好比,“kang.o”的依赖文件为:“kang.c”、“kang.h”、“head.h”。若是这些文件中任何一个的时间戳比“kang.o”新,则命令“gcc –Wall –O -g –c kang.c -o kang.o”将会执行,从而更新文件“kang.o”。在更新完“kang.o”或“yul.o”以后,make会检查最初的“kang.o”、“yul.o”和“sunq”三个文件,只要文件“kang.o”或“yul.o”中的任比文件时间戳比“sunq”新,则第二行命令就会被执行。这样,make就完成了自动检查时间戳的工做,开始执行编译工做。这也就是Make工做的基本流程。

接下来,为了进一步简化编辑和维护Makefile,make容许在Makefile中建立和使用变量。变量是在Makefile中定义的名字,用来代替一个文本字符串,该文本字符串称为该变量的值。在具体要求下,这些值能够代替目标体、依赖文件、命令以及makefile文件中其它部分。在Makefile中的变量定义有两种方式:一种是递归展开方式,另外一种是简单方式。

递归展开方式定义的变量是在引用在该变量时进行替换的,即若是该变量包含了对其余变量的应用,则在引用该变量时一次性将内嵌的变量所有展开,虽然这种类型的变量可以很好地完成用户的指令,可是它也有严重的缺点,如不能在变量后追加内容(由于语句:CFLAGS = $(CFLAGS) -O在变量扩展过程当中可能致使无穷循环)。

为了不上述问题,简单扩展型变量的值在定义处展开,而且只展开一次,所以它不包含任何对其它变量的引用,从而消除变量的嵌套引用。

递归展开方式的定义格式为:VAR=var

简单扩展方式的定义格式为:VAR:=var

Make中的变量使用均使用格式为:$(VAR)

 

 

注意

变量名是不包括“:”、“#”、“=”结尾空格的任何字符串。同时,变量名中包含字母、数字以及下划线之外的状况应尽可能避免,由于它们可能在未来被赋予特别的含义。

变量名是大小写敏感的,例如变量名“foo”、“FOO”、和“Foo”表明不一样的变量。

推荐在makefile内部使用小写字母做为变量名,预留大写字母做为控制隐含规则参数或用户重载命令选项参数的变量名。

 

下面给出了上例中用变量替换修改后的Makefile,这里用OBJS代替kang.o和yul.o,用CC代替Gcc,用CFLAGS代替“-Wall -O –g”。这样在之后修改时,就能够只修改变量定义,而不须要修改下面的定义实体,从而大大简化了Makefile维护的工做量。

经变量替换后的Makefile以下所示:

 

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $(OBJS) -o sunq

kang.o : kang.c kang.h

$(CC) $(CFLAGS) -c kang.c -o kang.o

yul.o : yul.c yul.h

$(CC) $(CFLAGS) -c yul.c -o yul.o

 

能够看到,此处变量是以递归展开方式定义的。

Makefile中的变量分为用户自定义变量、预约义变量、自动变量及环境变量。如上例中的OBJS就是用户自定义变量,自定义变量的值由用户自行设定,而预约义变量和自动变量为一般在Makefile都会出现的变量,其中部分有默认值,也就是常见的设定值,固然用户能够对其进行修改。

预约义变量包含了常见编译器、汇编器的名称及其编译选项。下表3.14列出了Makefile中常见预约义变量及其部分默认值。

表3.14 Makefile中常见预约义变量

命 令 格 式

含义

AR

库文件维护程序的名称,默认值为ar

AS

汇编程序的名称,默认值为as

CC

C编译器的名称,默认值为cc

CPP

C预编译器的名称,默认值为$(CC) –E

CXX

C++编译器的名称,默认值为g++

FC

FORTRAN编译器的名称,默认值为f77

RM

文件删除程序的名称,默认值为rm –f

ARFLAGS

库文件维护程序的选项,无默认值

ASFLAGS

汇编程序的选项,无默认值

CFLAGS

C编译器的选项,无默认值

CPPFLAGS

C预编译的选项,无默认值

CXXFLAGS

C++编译器的选项,无默认值

FFLAGS

FORTRAN编译器的选项,无默认值

 

能够看出,上例中的CC和CFLAGS是预约义变量,其中因为CC没有采用默认值,所以,须要把“CC=Gcc”明确列出来。

因为常见的Gcc编译语句中一般包含了目标文件和依赖文件,而这些文件在Makefile文件中目标体的一行已经有所体现,所以,为了进一步简化Makefile的编写,就引入了自动变量。自动变量一般能够表明编译语句中出现目标文件和依赖文件等,而且具备本地含义(即下一语句中出现的相同变量表明的是下一语句的目标文件和依赖文件)。下表3.15列出了Makefile中常见自动变量。

表3.15Makefile中常见自动变量

命令格式

含义

$*

不包含扩展名的目标文件名称

$+

全部的依赖文件,以空格分开,并以出现的前后为序,可能包含重复的依赖文件

$<

第一个依赖文件的名称

$?

全部时间戳比目标文件晚的依赖文件,并以空格分开

命令格式

含义

$@

目标文件的完整名称

$^

全部不重复的依赖文件,以空格分开

$%

若是目标是归档成员,则该变量表示目标的归档成员名称

 

自动变量的书写比较难记,可是在熟练了以后会很是的方便,请读者结合下例中的自动变量改写的Makefile进行记忆。

 

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

kang.o : kang.c kang.h

$(CC) $(CFLAGS) -c $< -o $@

yul.o : yul.c yul.h

$(CC) $(CFLAGS) -c $< -o $@

 

另外,在Makefile中还能够使用环境变量。使用环境变量的方法相对比较简单,make在启动时会自动读取系统当前已经定义了的环境变量,而且会建立与之具备相同名称和数值的变量。可是,若是用户在Makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量。

Makefile规则

Makefile的规则是Make进行处理的依据,它包括了目标体、依赖文件及其之间的命令语句。通常的,Makefile中的一条语句就是一个规则。在上面的例子中,都显示地指出了Makefile中的规则关系,如“$(CC) $(CFLAGS) -c $< -o $@”,但为了简化Makefile的编写,make还定义了隐式规则和模式规则,下面就分别对其进行讲解。

1.隐式规则

隐含规则可以告诉make怎样使用传统的技术完成任务,这样,当用户使用它们时就没必要详细指定编译的具体细节,而只需把目标文件列出便可。Make会自动搜索隐式规则目录来肯定如何生成目标文件。如上例就能够写成:

 

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

 

为何能够省略后两句呢?由于Make的隐式规则指出:全部“.o”文件均可自动由“.c”文件使用命令“$(CC) $(CPPFLAGS) $(CFLAGS) -c file.c –o file.o”生成。这样“kang.o”和“yul.o”就会分别调用“$(CC) $(CFLAGS) -c kang.c -o kang.o”和“$(CC) $(CFLAGS) -c yul.c -o yul.o”生成。

 

 

注意

在隐式规则只能查找到相同文件名的不一样后缀名文件,如”kang.o”文件必须由”kang.c”文件生成。

 

下表3.16给出了常见的隐式规则目录:

表3.16 Makefile中常见隐式规则目录

对应语言后缀名

规则

C编译:.c变为.o

$(CC) –c $(CPPFLAGS) $(CFLAGS)

C++编译:.cc或.C变为.o

$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)

Pascal编译:.p变为.o

$(PC) -c $(PFLAGS)

Fortran编译:.r变为-o

$(FC) -c $(FFLAGS)

2.模式规则

模式规则是用来定义相同处理规则的多个文件的。它不一样于隐式规则,隐式规则仅仅可以用make默认的变量来进行操做,而模式规则还能引入用户自定义变量,为多个文件创建相同的规则,从而简化Makefile的编写。

模式规则的格式相似于普通规则,这个规则中的相关文件前必须用“%”标明。使用模式规则修改后的Makefile的编写以下:

 

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

%.o : %.c

$(CC) $(CFLAGS) -c $< -o $@

Make使用

使用make管理器很是简单,只需在make命令的后面键入目标名便可创建指定的目标,若是直接运行make,则创建Makefile中的第一个目标。

此外make还有丰富的命令行选项,能够完成各类不一样的功能。下表3.17列出了经常使用的make命令行选项。

表3.17 make的命令行选项

命令格式

含 义

-C dir

读入指定目录下的Makefile

-f file

读入当前目录下的file文件做为Makefile

命令格式

含 义

-i

忽略全部的命令执行错误

-I dir

指定被包含的Makefile所在目录

-n

只打印要执行的命令,但不执行这些命令

-p

显示make变量数据库和隐含规则

-s

在执行命令时不显示命令

-w

若是make在执行过程当中改变目录,则打印当前目录名

使用autotools

在上一小节,读者已经了解到了make项目管理器的强大功能。的确,Makefile能够帮助make完成它的使命,但要认可的是,编写Makefile确实不是一件轻松的事,尤为对于一个较大的项目而言更是如此。那么,有没有一种轻松的手段生成Makefile而同时又能让用户享受make的优越性呢?本节要讲的autotools系列工具正是为此而设的,它只需用户输入简单的目标文件、依赖文件、文件目录等就能够轻松地生成Makefile了,这无疑是广大用户的所但愿的。另外,这些工具还能够完成系统配置信息的收集,从而能够方便地处理各类移植性的问题。也正是基于此,如今Linux上的软件开发通常都用autotools来制做Makefile,读者在后面的讲述中就会了解到。

autotools使用流程

正如前面所言,autotools是系列工具,读者首先要确认系统是否装了如下工具(能够用which命令进行查看)。

· aclocal

· autoscan

· autoconf

· autoheader

· automake

使用autotools主要就是利用各个工具的脚本文件以生成最后的Makefile。其整体流程是这样的:

· 使用aclocal生成一个“aclocal.m4”文件,该文件主要处理本地的宏定义;

· 改写“configure.scan”文件,并将其重命名为“configure.in”,并使用autoconf文件生成configure文件。

接下来,笔者将经过一个简单的hello.c例子带领读者熟悉autotools生成makefile的过程,因为在这过程当中有涉及到较多的脚本文件,为了更清楚地了解相互之间的关系,强烈建议读者实际动手操做以体会其整个过程。

1.autoscan

它会在给定目录及其子目录树中检查源文件,若没有给出目录,就在当前目录及其子目录树中进行检查。它会搜索源文件以寻找通常的移植性问题并建立一个文件“configure.scan”,该文件就是接下来autoconf要用到的“configure.in”原型。以下所示:

 

[root@localhost automake]# autoscan

autom4te: configure.ac: no such file or directory

autoscan: /usr/bin/autom4te failed with exit status: 1

[root@localhost automake]# ls

autoscan.log configure.scan hello.c

 

如上所示,autoscan首先会尝试去读入“configure.ac”(同configure.in的配置文件)文件,此时尚未建立该配置文件,因而它会自动生成一个“configure.in”的原型文件“configure.scan”。

2.autoconf

configure.in是autoconf的脚本配置文件,它的原型文件“configure.scan”以下所示:

 

# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.59)

#The next one is modified by sunq

#AC_INIT(FULL-PACKAGE-NAME,VERSION,BUG-REPORT-ADDRESS)

AC_INIT(hello,1.0)

# The next one is added by sunq

AM_INIT_AUTOMAKE(hello,1.0)

AC_CONFIG_SRCDIR([hello.c])

AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_CONFIG_FILES([Makefile])

AC_OUTPUT

 

下面对这个脚本文件进行解释:

· 以“#”号开始的行为注释。

· AC_PREREQ宏声明本文件要求的autoconf版本,如本例使用的版本2.59。

· AC_INIT宏用来定义软件的名称和版本等信息,在本例中省略了BUG-REPORT-ADDRESS,通常为做者的e-mail。

· AM_INIT_AUTOMAKE是笔者另加的,它是automake所必备的宏,也同前面同样,PACKAGE是所要产生软件套件的名称,VERSION是版本编号。

· AC_CONFIG_SRCDIR宏用来侦测所指定的源码文件是否存在,来肯定源码目录的有

效性。在此处为当前目录下的hello.c。

· AC_CONFIG_HEADER宏用于生成config.h文件,以便autoheader使用。

· AC_CONFIG_FILES宏用于生成相应的Makefile文件。

· 中间的注释间能够添加分别用户测试程序、测试函数库、测试头文件等宏定义。

接下来首先运行aclocal,生成一个“aclocal.m4”文件,该文件主要处理本地的宏定义。以下所示:

 

[root@localhost automake]# aclocal

 

再接着运行autoconf,生成“configure”可执行文件。以下所示:

 

[root@localhost automake]# autoconf

[root@localhost automake]# ls

aclocal.m4 autom4te.cache autoscan.log configure configure.in hello.c

3.autoheader

接着使用autoheader命令,它负责生成config.h.in文件。该工具一般会从“acconfig.h”文件中复制用户附加的符号定义,所以此处没有附加符号定义,因此不须要建立“acconfig.h”文件。以下所示:

 

[root@localhost automake]# autoheader

4.automake

这一步是建立Makefile很重要的一步,automake要用的脚本配置文件是Makefile.am,用户须要本身建立相应的文件。以后,automake工具转换成Makefile.in。在该例中,笔者建立的文件为Makefile.am以下所示:

 

AUTOMAKE_OPTIONS=foreign

bin_PROGRAMS= hello

hello_SOURCES= hello.c

 

下面对该脚本文件的对应项进行解释。

· 其中的AUTOMAKE_OPTIONS为设置automake的选项。因为GNU(在第1章中已经有所介绍)对本身发布的软件有严格的规范,好比必须附带许可证声明文件COPYING等,不然automake执行时会报错。automake提供了三种软件等级:foreign、gnu和gnits,让用户选择采用,默认等级为gnu。在本例使用foreign等级,它只检测必须的文件。

· bin_PROGRAMS定义要产生的执行文件名。若是要产生多个执行文件,每一个文件名用空格隔开。

· hello_SOURCES定义“hello”这个执行程序所须要的原始文件。若是”hello”这个程序是由多个原始文件所产生的,则必须把它所用到的全部原始文件都列出来,并用空格隔开。例如:若目标体“hello”须要“hello.c”、“sunq.c”、“hello.h”三个依赖文件,则定义hello_SOURCES=hello.c sunq.c hello.h。要注意的是,若是要定义多个执行文件,则对每一个执行程序都要定义相应的file_SOURCES。

接下来能够使用automake对其生成“configure.in”文件,在这里使用选项“—adding-missing”可让automake自动添加有一些必需的脚本文件。以下所示:

 

[root@localhost automake]# automake --add-missing

configure.in: installing './install-sh'

configure.in: installing './missing'

Makefile.am: installing 'depcomp'

[root@localhost automake]# ls

aclocal.m4 autoscan.log configure.in hello.c Makefile.am missing

autom4te.cache configure depcomp install-sh Makefile.in config.h.in

 

能够看到,在automake以后就能够生成configure.in文件。

5.运行configure

在这一步中,经过运行自动配置设置文件configure,把Makefile.in变成了最终的Makefile。以下所示:

 

[root@localhost automake]# ./configure

checking for a BSD-compatible install... /usr/bin/install -c

checking whether build enVironment is sane... yes

checking for gawk... gawk

checking whether make sets $(MAKE)... yes

checking for Gcc... Gcc

checking for C compiler default output file name... a.out

checking whether the C compiler works... yes

checking whether we are cross compiling... no

checking for suffix of executables...

checking for suffix of object files... o

checking whether we are using the GNU C compiler... yes

checking whether Gcc accepts -g... yes

checking for Gcc option to accept ANSI C... none needed

checking for style of include used by make... GNU

checking dependency style of Gcc... Gcc3

configure: creating ./config.status

config.status: creating Makefile

config.status: executing depfiles commands

能够看到,在运行configure时收集了系统的信息,用户能够在configure命令中对其进行方便地配置。在./configure的自定义参数有两种,一种是开关式(--enable-XXX或--disable-XXX),另外一种是开放式,即后面要填入一串字符(--with-XXX=yyyy)参数。读者能够自行尝试其使用方法。另外,读者能够查看同一目录下的”config.log”文件,以方便调试之用。

到此为止,makefile就能够自动生成了。回忆整个步骤,用户再也不须要定制不一样的规则,而只须要输入简单的文件及目录名便可,这样就大大方便了用户的使用。下面的图3.9总结了上述过程:

 

图3.9 autotools生成Makefile流程图

使用autotools所生成的Makefile

autotools生成的Makefile除具备普通的编译功能外,还具备如下主要功能(感兴趣的读者能够查看这个简单的hello.c程序的makefile):

1.make

键入make默认执行”make all”命令,即目标体为all,其执行状况以下所示:

 

[root@localhost automake]# make

if Gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -MT hello.o -MD -MP -MF ".deps/hello.Tpo" -c -o hello.o hello.c;

then mv -f ".deps/hello.Tpo" ".deps/hello.Po"; else rm -f ".deps/hello.Tpo"; exit 1; fi

Gcc -g -O2 -o hello hello.o

此时在本目录下就生成了可执行文件“hello”,运行“./hello”能出现正常结果,以下所示:

 

[root@localhost automake]# ./hello

Hello!Autoconf!

2.make install

此时,会把该程序安装到系统目录中去,以下所示:

 

[root@localhost automake]# make install

if Gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -MT hello.o -MD -MP -MF ".deps/hello.Tpo" -c -o hello.o hello.c;

then mv -f ".deps/hello.Tpo" ".deps/hello.Po"; else rm -f ".deps/hello.Tpo"; exit 1; fi

Gcc -g -O2 -o hello hello.o

make[1]: Entering directory '/root/workplace/automake'

test -z "/usr/local/bin" || mkdir -p -- "/usr/local/bin"

/usr/bin/install -c 'hello' '/usr/local/bin/hello'

make[1]: Nothing to be done for 'install-data-am'.

make[1]: LeaVing directory '/root/workplace/automake'

 

此时,若直接运行hello,也能出现正确结果,以下所示:

 

[root@localhost automake]# hello

Hello!Autoconf!

3.make clean

此时,make会清除以前所编译的可执行文件及目标文件(object file, *.o),以下所示:

 

[root@localhost automake]# make clean

test -z "hello" || rm -f hello

rm -f *.o

4.make dist

此时,make将程序和相关的文档打包为一个压缩文档以供发布,以下所示:

 

[root@localhost automake]# make dist

[root@localhost automake]# ls hello-1.0-tar.gz

hello-1.0-tar.gz

 

可见该命令生成了一个hello-1.0-tar.gz的压缩文件。

由上面的讲述读者不难看出,autotools确实是软件维护与发布的必备工具,也鉴于此,现在GUN的软件通常都是由automake来制做的。

 

 

想想

对于automake制做的这类软件,应如何安装呢?

Vi使用练习

1.实验目的

经过指定指令的Vi操做练习,使读者可以熟练使用Vi中的常见操做,而且熟悉Vi的三种模式,若是读者可以熟练掌握实验内容中所要求的内容,则代表对Vi的操做已经很熟练了。

2.实验内容

(1)在“/root”目录下建一个名为“/Vi”的目录。

(2)进入“/Vi”目录。

(3)将文件“/etc/inittab”复制到“/Vi”目录下。

(4)使用Vi打开“/Vi”目录下的inittab。

(5)设定行号,指出设定initdefault(相似于“id:5:initdefault”)的所在行号。

(6)将光标移到该行。

(7)复制该行内容。

(8)将光标移到最后一行行首。

(9)粘贴复制行的内容。

(10)撤销第9步的动做。

(11)将光标移动到最后一行的行尾。

(12)粘贴复制行的内容。

(13)光标移到“si::sysinit:/etc/rc.d/rc.sysinit”。

(14)删除该行。

(15)存盘但不退出。

(16)将光标移到首行。

(17)插入模式下输入“Hello,this is Vi world!”。

(18)返回命令行模式。

(19)向下查找字符串“0:wait”。

(20)再向上查找字符串“halt”。

(21)强制退出Vi,不存盘。

分别指出每一个命令处于何种模式下?

3.实验步骤

(1)mkdir /root/Vi

(2)cd /root/Vi

(3)cp /etc/inittab ./

(4)Vi ./inittab

(5):set nu(底行模式)

(6)17<enter>(命令行模式)

(7)yy

(8)G

(9)p

(10)u

(11)$

(12)p

(13)21G

(14)dd

(15):w(底行模式)

(16)1G

(17)i 并输入“Hello,this is Vi world!”(插入模式)

(18)Esc

(19)/0:wait(命令行模式)

(20)?halt

(21):q!(底行模式)

4.实验结果

该实验最后的结果只对“/root/inittab”增长了一行复制的内容:“id:5:initdefault”。

用Gdb调试有问题的程序

1.实验目的

经过调试一个有问题的程序,使读者进一步熟练使用Vi操做,并且熟练掌握Gcc编译命令及Gdb的调试命令,经过对有问题程序的跟踪调试,进一步提升发现问题和解决问题的能力。这是一个很小的程序,只有35行,但愿读者认真调试。

2.实验内容

(1)使用Vi编辑器,将如下代码输入到名为greet.c的文件中。此代码的原意为输出倒序main函数中定义的字符串,但结果显示没有输出。代码以下所示:

 

#include <stdio.h>

int display1(char *string);

int display2(char *string);

 

int main ()

{

char string[] = "Embedded Linux";

display1 (string);

display2 (string);

}

int display1 (char *string)

{

printf ("The original string is %s n", string);

}

int display2 (char *string1)

{

char *string2;

int size,i;

size = strlen (string1);

string2 = (char *) malloc (size + 1);

for (i = 0; i < size; i++)

string2[size - i] = string1[i];

string2[size+1] = ' ';

printf("The string afterward is %sn",string2);

}

 

(2)使用Gcc编译这段代码,注意要加上“-g”选项以方便以后的调试。

(3)运行生成的可执行文件,观察运行结果。

(4)使用Gdb调试程序,经过设置断点、单步跟踪,一步步找出错误所在。

(5)纠正错误,更改源程序并获得正确的结果。

3.实验步骤

(1)在工做目录上新建文件greet.c,并用Vi启动:vi greet.c。

(2)在Vi中输入以上代码。

(3)在Vi中保存并退出:wq。

(4)用Gcc编译:gcc -g greet.c -o greet。

(5)运行greet:./greet,输出为:

 

The original string is Embedded Linux

The string afterward is

 

可见,该程序没有可以倒序输出。

(6)启动Gdb调试:gdb greet。

(7)查看源代码,使用命令“l”。

(8)在30行(for循环处)设置断点,使用命令“b 30”。

(9)在33行(printf函数处)设置断点,使用命令“b 33”。

(10)查看断点设置状况,使用命令“info b”。

(11)运行代码,使用命令“r”。

(12)单步运行代码,使用命令“n”。

(13)查看暂停点变量值,使用命令“p string2[size - i]”。

(14)继续单步运行代码数次,并使用命令查看,发现string2[size-1]的值正确。

(15)继续程序的运行,使用命令“c”。

(16)程序在printf前中止运行,此时依次查看string2[0]、string2[1]…,发现string[0]没有被正确赋值,然后面的复制都是正确的,这时,定位程序第31行,发现程序运行结果错误的缘由在于“size-1”。因为i只能增到“size-1”,这样string2[0]就永远不能被赋值而保持NULL,故输不出任何结果。

(17)退出Gdb,使用命令q。

(18)从新编辑greet.c,把其中的“string2[size - i] = string1[i]”改成“string2[size – i - 1] = string1[i];”便可。

(19)使用Gcc从新编译:gcc -g greet.c -o greet。

(20)查看运行结果:./greet

 

The original string is Embedded Linux

The string afterward is xuniL deddedbmE

 

这时,输入结果正确。

4.实验结果

将原来有错的程序通过Gdb调试,找出问题所在,并修改源代码,输出正确的倒序显示字符串的结果。

编写包含多文件的Makefile

1.实验目的

经过对包含多文件的Makefile的编写,熟悉各类形式的Makefile,而且进一步加深对Makefile中用户自定义变量、自动变量及预约义变量的理解。

2.实验过程

(1)用Vi在同一目录下编辑两个简单的Hello程序,以下所示:

 

#hello.c

#include "hello.h"

int main()

{

printf("Hello everyone!n");

}

#hello.h

#include <stdio.h>

 

(2)仍在同一目录下用Vi编辑Makefile,且不使用变量替换,用一个目标体实现(即直接将hello.c和hello.h编译成hello目标体)。而后用make验证所编写的Makefile是否正确。

(3)将上述Makefile使用变量替换实现。一样用make验证所编写的Makefile是否正确

(4)用编辑另外一Makefile,取名为Makefile1,不使用变量替换,但用两个目标体实现(也就是首先将hello.c和hello.h编译为hello.o,再将hello.o编译为hello),再用make的”-f”选项验证这个Makefile1的正确性。

(5)将上述Makefile1使用变量替换实现。

3.实验步骤

(1)用Vi打开上述两个代码文件“hello.c”和“hello.h”。

(2)在shell命令行中用Gcc尝试编译,使用命令:”Gcc hello.c –o hello”,并运行hello可执行文件查看结果。

(3)删除这次编译的可执行文件:rm hello。

(4)用Vi编辑Makefile,以下所示:

 

hello:hello.c hello.h

Gcc hello.c -o hello

 

(5)退出保存,在shell中键入:make,查看结果。

(6)再次用Vi打开Makefile,用变量进行替换,以下所示:

 

OBJS :=hello.o

CC :=Gcc

hello:$(OBJS)

$(CC) $^ -o $@

 

(7)退出保存,在shell中键入:make,查看结果。

(8)用Vi编辑Makefile1,以下所示:

 

hello:hello.o

Gcc hello.o -o hello

hello.o:hello.c hello.h

Gcc -c hello.c -o hello.o

 

(9)退出保存,在shell中键入:make -f Makefile1,查看结果。

(10)再次用Vi编辑Makefile1,以下所示:

 

OBJS1 :=hello.o

OBJS2 :=hello.c hello.h

CC :=Gcc

hello:$(OBJS1)

$(CC) $^ -o $@

$(OBJS1):$(OBJS2)

$(CC) -c $< -o $@

 

在这里请注意区别“$^”和“$<”。

(11)退出保存,在shell中键入:make -f Makefile1,查看结果

4.实验结果

各类不一样形式的makefile都能完成其正确的功能。

使用autotools生成包含多文件的Makefile

1.实验目的

经过使用autotools生成包含多文件的Makefile,进一步掌握autotools的正确使用方法。同时,掌握Linux下安装软件的经常使用方法。

2.实验过程

(1)在原目录下新建文件夹auto。

(2)利用上例的两个代码文件“hello.c”和“hello.h”,并将它们复制到该目录下。

(3)使用autoscan生成configure.scan。

(4)编辑configure.scan,修改相关内容,并将其重命名为configure.in。

(5)使用aclocal生成aclocal.m4。

(6)使用autoconf生成configure。

(7)使用autoheader生成config.in.h。

(8)编辑Makefile.am。

(9)使用automake生成Makefile.in。

(10)使用configure生成Makefile。

(11)使用make生成hello可执行文件,并在当前目录下运行hello查看结果。

(12)使用make install将hello安装到系统目录下,并运行,查看结果。

(13)使用make dist生成hello压缩包。

(14)解压hello压缩包。

(15)进入解压目录。

(16)在该目录下安装hello软件。

3.实验步骤

(1)mkdir ./auto。

(2)cp hello.* ./auto(假定原先在“hello.c”文件目录下)。

(3)命令:autoscan。

(4)使用Vi编辑configure.scan为:

 

# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

 

AC_PREREQ(2.59)

AC_INIT(hello, 1.0)

AM_INIT_AUTOMAKE(hello,1.0)

AC_CONFIG_SRCDIR([hello.h])

AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT(Makefile)

 

(5)保存退出,并重命名为configure.in。

(6)运行:aclocal。

(7)运行:autoconf,并用ls查看是否生成了configure可执行文件。

(8)运行:autoheader。

(9)用Vi编辑Makefile.am文件为:

 

AUTOMAKE_OPTIONS=foreign

bin_PROGRAMS=hello

hello_SOURCES=hello.c hello.h

 

(10)运行:automake。

(11)运行:./configure。

(12)运行:make。

(13)运行:./hello,查看结果是否正确。

(14)运行:make install。

(15)运行:hello,查看结果是否正确。

(16)运行:make dist。

(17)在当前目录下解压hello-1.0.tar.gz:tar –zxvf hello-1.0.tar.gz。

(18)进入解压目录:cd ./hello-1.0。

(19)下面开始Linux下常见的安装软件步骤:./configure。

(20)运行:make。

(21)运行:./hello(在正常安装时这一步可省略)。

(22)运行:make install。

(23)运行:hello,查看结果是否正确。

4.实验结果

可以正确使用autotools生成Makefile,而且可以安装成功短小的Hello软件。

 

 

在shell上经过man gcc命令能够查看manpage文档。

本身根据平时所学分享一些经常使用的命令使用,请你们批评指正!

1. gcc -E sourceFile.c
-E,只预编译。直接输出预编译结果。


-E参数,进行预编译时,将输出信息,将程序所包含的头文件,函数,宏定义等,进行扩展。

 

2. gcc -S sourceFile.c 
-S,只执行到源代码到汇编代码的转换,输出汇编代码。


3. gcc -c source_file.c
-c,只执行到编译,输出目标文件。


汇编知识将在之后的博客推出。


4. gcc -c sourceFile.c -o outputFileName
-o, 指定输出文件名 该参数能够省略。默认下(gcc sourceFile.c):生成名为a.out的可执行文件。
-c:生成名为sourceFile.o的目标文件。(进行编译,不连接)

 

5. gcc -g sourceFile.c 
-g,生成供调试用的可执行文件,能够在gdb中运行。
用strip命令从新将debug信息清除。这是会发现生成的文件比正常编译的输出小。

这是由于strip把原先正常编译中的一些额外信息(如函数名之类)去除。

 

6. gcc -s sourceFile.c
-s效果与strip相同。


7. gcc -O source_file.c
-O(大写的字母O),编译器对代码进行自动优化编译,输出效率更高的可执行文件。
-O 后面还能够跟上数字指定优化级别,如:
gcc -O2 source_file.c
通常可选择2;3会有必定风险。(这里不作演示)


8. gcc -Wall source_file.c
-W,在编译中开启一些额外的警告(warning)信息。-Wall,打开全部的警告信息。


9. gcc source_file.c -L/XXX/lib -llibXXX.a -I/XXX/include
-l, 指定所使用到的函数库,连接器将连接名为libxxx.a(后缀.a表示静态库)的函数库。
-L,指定函数库所在的文件夹,连接器会搜索/XXX/lib(通常能够指定路径)文件夹。
-I, 指定头文件所在的文件夹,预编译器会搜索/XXX/include文件夹。

10.gcc -D MAX_SIZE=value sourceFile.c 
预约义名为MAX_SIZE ,值为value的宏。

 

若不指定MAX_SIZE的值,默认为1

 
来源:CSDN
原文:https://blog.csdn.net/m1223853767/article/details/79464729
 

1、C/C++文件的编译过程:
先来看一下gcc的使用方法和经常使用选项
提示:gcc --help

Ⅰ、使用方法:
gcc [选项] 文件名
Ⅱ、经常使用选项:
选项 含义
-v 查看gcc编译器的版本,显示gcc执行时的详细过程
-o <file> Place the output into <file>;指定输出文件名为file,这个名称不能跟源文件名同名
-E Preprocess only; do not compile, assemble or link;只预处理,不会编译、汇编、连接
-S Compile only; do not assemble or link;只编译,不会汇编、连接
-c Compile and assemble, but do not link; 编译和汇编,不会连接
      一个C/C++文件要通过预处理(preprocessing)、编译(compilation)、汇编(assembly)、和链接(linking)才能变成可执行文件。
如下列程序为例,追层来分析编译过程。
hello.c:

#include <stdio.h>

#define MAX 20
#define MIN 10

#define _DEBUG
#define SetBit(x) (1<<x)

int main(int argc, char* argv[])
{
printf("Hello World \n");
printf("MAX = %d,MIN = %d,MAX + MIN = %d\n",MAX,MIN,MAX + MIN);

#ifdef _DEBUG
printf("SetBit(5) = %d,SetBit(6) = %d\n",SetBit(5),SetBit(6));
printf("SetBit( SetBit(2) ) = %d\n",SetBit( SetBit(2) ));
#endif
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
① 预处理:
gcc -E -o hello.i hello.c


预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些代码输出到一个“.i”文件中等待进一步处理。
② 编译:
gcc -S -o hello.s hello.i


编译就是把C/C++代码(好比上面的”.i”文件)“翻译”成汇编代码。
③ 汇编:
gcc -c -o hello.o hello.s


.o:object file(OBJ文件) 这里表现为二进制目标文件:


汇编就是将第二步输出的汇编代码翻译成符合必定格式的机器代码,在Linux系统上通常表现位ELF目标文件(OBJ文件)。
④ 连接:
gcc -o hello hello.o


连接就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件连接起来,最终生成能够在特定平台运行的可执行程序。
总结:在编译过程当中。除非使用了”-c”,“-S”,或”-E”选项(或者编译错误阻止了完整的过程),不然统一完整连接步骤。
譬如:gcc hello.c 和gcc -o hello hello.c都已经完成连接操做。


又如:gcc -c -o hello.o hello.c


2、连接原理:
gcc -c -o hello.o hello.c 不做最后一步连接,获得hello.o二进制OBJ文件
gcc -v -o hello hello.o 咱们来看同样连接过程是怎样的:
book@www.100ask.org:/work/gcc_options/1th$ gcc -v -o hello hello.o
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.4' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'hello' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccbhavbV.res
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s
-plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc
-plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id
--eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed
-dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro
-o hello
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o
-L/usr/lib/gcc/x86_64-linux-gnu/5
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib
-L/lib/x86_64-linux-gnu -L/lib/../lib
-L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../..
hello.o
-lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
book@www.100ask.org:/work/gcc_options/1th$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o是gcc加入的系统标准启动文件,对于通常应用程序,这些启动是必需的。
-lc:连接libc库文件,其中libc库文件中就实现了printf等函数。
① 动态连接:动态连接使用动态连接库进行连接,生成的程序在执行的时候须要加载所需的动态库才能运行。  动态连接生成的程序体积较小,可是必须依赖所需的动态库,不然没法执行。
默认使用动态连接:gcc -o hello_shared hello.o

 

② 静态连接:静态连接使用静态库进行连接,生成的程序包含程序运行所须要的所有库,能够直接运行,不过静态连接生成的程序体积较大。
gcc -static -o hello_static hello.o

 

③ -nostartfiles
不连接系统标准启动文件,而标准库文件仍然正常使用:
gcc -v -nostartfiles -o hello hello.o

 

④ -nostdlib(最经常使用)
不连接系统标准启动文件和标准库文件:
gcc -v -nostdlib -o hello hello.o


- 会提示由于没有连接系统标准启动文件和标准库文件,而连接失败。
- 这个-nostdlib选项经常使用于裸机/bootloader、linux内核等程序,由于它们不须要启动文件、标准库文件。

 
来源:CSDN
原文:https://blog.csdn.net/czg13548930186/article/details/78331692
 

 

    .c为后缀的文件,C语言源代码文件;
    .a为后缀的文件,是由目标文件构成的档案库文件;
    .C,.cc或.cxx 为后缀的文件,是C++源代码文件;
    .h为后缀的文件,是程序所包含的头文件;
    .i 为后缀的文件,是已经预处理过的C源代码文件;
    .ii为后缀的文件,是已经预处理过的C++源代码文件;
    .m为后缀的文件,是Objective-C源代码文件;
    .o为后缀的文件,是编译后的目标文件;
    .s为后缀的文件,是汇编语言源代码文件;
    .S为后缀的文件,是通过预编译的汇编语言源代码文件。

 

    gcc的基本用法和选项
    在使用gcc编译器的时候,咱们必须给出一系列必要的调用参数和文件名称。gcc编译器的调用参数大约有100多个,其中多数参数咱们可能根本就用不到,这里只介绍其中最基本、最经常使用的参数。
    gcc最基本的用法是∶gcc [options] [filenames]
    其中options就是编译器所须要的参数,filenames给出相关的文件名称。
    -c,只编译,不链接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,一般用于编译不包含主程序的子程序文件。
    -o output_filename,肯定输出文件的名称为output_filename,同时这个名称不能和源文件同名。若是不给出这个选项,gcc就给出预设的可执行文件a.out。
    -g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,咱们就必须加入这个选项。
    -O,对程序进行优化编译、链接,采用这个选项,整个源代码会在编译、链接过程当中进行优化处理,这样产生的可执行文件的执行效率能够提升,可是,编译、链接的速度就相应地要慢一些。
    -O2,比-O更好的优化编译、链接,固然整个编译、链接过程会更慢。
    -Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程当中使用的参数。
    C程序中的头文件包含两种状况∶
    A)#include
    B)#include “myinc.h”
 

   这时,预编译、编译链接一次完成,生成一个系统预设的名为a.out的可执行文件,对于稍为复杂的状况,好比有多个源代码文件、须要链接档案库或者有其余比较特别的要求,就要给定适当的调用选项参数。再看一个简单的例子。整个源代码程序由两个文件testmain.c 和testsub.c组成,程序中使用了系统提供的数学库,同时但愿给出的可执行文件为test,这时的编译命令能够是∶
    gcc testmain.c testsub.c -lm -o test
    其中,-lm表示链接系统的数学库libm.a,这个过程能够用图12-1框图描述。

 
 

gcc经常使用编译选项

-g : 加入调试信息

-m64 : 64位

-c : 只作预处理、编译和汇编,不连接,产生的是目标文件(.o文件)

-S : 只作预处理和编译,把文件编译成为汇编代码

-include : 某个代码,简单来讲,就是便以某个文件,须要另外一个文件的时候,就能够用它设定,功能就至关于在代码中使用#include ,例如gcc hello.c -include /root/test.h

-I : 程序中用#include”file”的时候,gcc/g++会先在当前目录查找你所制定的头文件,若是没有找到,他回到缺省的头文件目录找,若是使用-I指定了目录,他会先在你-I后所指定的目录查找,而后再按常规的顺序去找

-I$(PATH) : inlcude,PATH指定一个环境变量的值

-fPIC : 该选项用于生成位置无关的代码

-shared : 将-fPIC生成的位置无关的代码做为动态库,通常状况下,-fPIC和-shared都是一块儿使用的。生成SO文件,共享库

-static : 此选项将禁止使用动态库,因此,编译出来的东西,通常都很大,也不须要什么动态链接库,就能够运行

-o : 指定程序的名字

-l : 指定so文件的名字,好比须要libcdaParser.so,就能够写成-lcdaParser,前面的lib和后面的.so能够省略

-L : 指定so文件所在的目录

-O : 编译器的优化选项,-O0表示不作优化,-O1为默认,-O3为最高优 化级别

 

 

一、gdb 经常使用命令

 

首先程序编译时加 -g 选项才可打开调试选项 
eg:gcc –o filename –Wall filename.c –g //进入调试
gdb filename //进入调试
l    //显示代码 (list)
b  4    //在第四行设置断点 至关于 Windows 的 F9 (break)           //若为 b  main  则表示断点打在main处
r    //运行   至关于 Windows 的 F5 (run)
n //下一步不进入函数 至关于 Windows 的 F10  (next)
s //表示单步进入函数, 至关于 Windows 的 F11 (step)
p  I  //打印变量 I 至关于 Windows 的 Watch 窗口(print)
c     //运行到最后(continue)
q     //退出 至关于 Windows 的   Shift+F5 (quit)

 
来源:CSDN
原文:https://blog.csdn.net/smilejiasmile/article/details/74946733
 

gcc的基本用法

命令格式:gcc [选项] [文件名]

编译的四个阶段:
-E:仅执行编译预处理;
-c:仅执行编译操做,不进行链接操做;
-S:将C代码转换为汇编代码;
-o:指定生成的输出文件。


–c是使用GNU汇编器将源文件转化为目标代码以后就结束,在这种状况下,只调用了C编译器(ccl)和汇编器(as),而链接器(ld)并无被执行,因此输出的目标文件不会包含做为Linux程序在被装载和执行时所必须的包含信息,但它能够在之后被链接到一个程序
-c表示只编译(compile),而不链接成为可执行文件。生成同名字的 .o 目标文件。一般用于编译不包含主程序的子程序文件。
gcc -c hello.c
生成:hello.o

-o选项用于说明输出(output)文件名,gcc将生成一个目标(object)文件xx。
gcc hello.c -o xqf
或者:gcc -o xqf hello.c(顺序能够调换)
输出:xqf 为程序可执行文件

-g 选项产生符号调试工具(GNU的gdb)所必要的符号信息,插入到生成的二进制代码中。表示编译DEBUG版本。
想要对源代码进行调试,就必须加入这个选项。固然,会增长可执行文件的大小。
gcc study.c -o xqf
gcc -g study.c -o xqf_g
结果以下:(确实加了 -g 可执行文件后变大了一点)
-rwxr-xr-x 1 root root 12393 Apr 19 21:39 xqf_g
-rwxr-xr-x 1 root root 11817 Apr 19 20:48 xqf

gcc 在产生调试符号时,一样采用了分级的思路,开发人员能够经过在 -g 选项后附加数字一、二、3指定在代码中加入调试信息的多少。默认的级别是2(-g2),此时产生的调试信息包括:扩展的符号表、行号、局部或外部变量信息。
级别3(-g3)包含级别2中的全部调试信息以及源代码中定义的宏。
级别1(-g1)不包含局部变量和与行号有关的调试信息,所以只可以用于回溯跟踪和堆栈转储。
回溯追踪:指的是监视程序在运行过程当中函数调用历史。
堆栈转储:则是一种以原始的十六进制格式保存程序执行环境的方法。


-pedantic 选项:当gcc在编译不符合ANSI/ISO C 语言标准的源代码时,将产生相应的警告信息
//study.c
#include <stdio.h>

int main()
{
long long int var = 1;
printf("hello world!\n");
return 0;
}

gcc -pedantic -o mm study.c
study.c: In function ‘main’:
study.c:5: warning: ISO C90 does not support ‘long long’


-Wall选项:使gcc产生尽量多的警告信息,警告信息颇有多是错误的来源,特别是隐式编程错误,因此尽可能保持0 warning。
用上面的代码:study.c,编译以下
gcc -Wall -o he study.c
study.c: In function ‘main’:
study.c:5: warning: unused variable ‘var’


-Werror 选项:要求gcc将全部的警告看成错误进行处理。
一样是上面的程序:study.c
gcc -Werror -o haha study.c
居然没有错误!!
改一下study.c
#include <stdio.h>


void main()
{
long long int var = 1;
printf("hello world!\n");
//return 0;
}


再编译:
gcc -Werror -o haha study.c
cc1: warnings being treated as errors
study.c: In function ‘main’:
study.c:4: error: return type of ‘main’ is not ‘int’

gcc -Wall -o hehe study.c
study.c:3: warning: return type of ‘main’ is not ‘int’
study.c: In function ‘main’:
study.c:5: warning: unused variable ‘var’

因此说:并非全部的warning都变成 error。具体的,后面再深究。

 

-fPIC选项。PIC指Position Independent Code。共享库要求有此选项,以便实现动态链接(dynamic linking)。


-I 选项(大写的 i):向头文件搜索目录中添加新的目录。
一、用#include"file"的时候,gcc/g++会先在当前目录查找你所制定的头文件,如
果没有找到,他回到缺省的头文件目录找。
若是使用-I制定了目录,他会先在你所制定的目录查找,而后再按常规的顺序去找.
二、用#include<file>,gcc/g++会到-I制定的目录查找,查找不到,而后将到系统的缺
省的头文件目录查找
例如:
gcc –I /usr/dev/mysql/include test.c –o test.o


-l选项(小写的 l)说明库文件的名字。若是库文件为 libtest.so, 则选项为: -ltest


-L选项说明库文件所在的路径。
例如:-L.(“.”表示当前路径)。
      -L/usr/lib (“/usr/lib” 为路径。注:这里的路径是绝对路径)
若是没有提供 -L选项,gcc 将在默认库文件路径下搜索


-shared选项指定生成动态链接库,不用该标志外部程序没法链接。至关于一个可执行文件, 生成 .so 文件


-static 选项,强制使用静态连接库,生成 .a 文件。由于gcc在连接时优先选择动态连接库,只有当动态连接库不存在时才使用静态连接库。加上该选项可强制使用静态连接库。
.so 和 .a 的区别:运行时动态加载,编译时静态加载
具体的例子在文章:linux so文件生成与连接中有讲。

 

多个文件一块儿编译:
文件:test_a.c  test_b.c
两种编译方法:
一、一块儿编译
gcc test_a.c test_b.c -o test

二、分别编译各个源文件,以后对编译后输出的目标文件连接
gcc -c test_a.c
gcc -c test_b.c
gcc -o test_a.o test_b.o -o test

比较:第一中方法编译时须要全部文件从新编译;第二种植从新编译修改的文件,未修改的不用从新编译。 来源:CSDN 原文:https://blog.csdn.net/xiaqunfeng123/article/details/51301749