gcc 命令行参数详解

时间:2021-08-19 15:34:28
1、gcc包含的c/c++编译器

gcc、cc、c++、g++
gcc和cc是一样的,c++和g++是一样的,一般c程序就用gcc编译,c++程序就用g++编译

2、gcc的基本用法

gcc test.c这样将编译出一个名为a.out的程序
gcc test.c -o test这样将编译出一个名为test的程序
-o参数用来指定生成程序的名字

3、为什么会出现undefined reference to 'xxxxx'错误?

首先这是链接错误,不是编译错误,也就是说如果只有这个错误,说明你的程序源码本身没有问题,是你用编译器编译时参数用得不对,你没有指定链接程序要用到得库,比如你的程序里用到了一些数学函数,那么你就要在编译参数里指定程序要链接数学库,方法是在编译命令行里加入-lm

4、-l参数和-L参数

-l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?-lname,在连接时,装载名字为“libname.a”的函数库:-lm表示连接名为“libm.a”的数学函数库。就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名了

好了现在我们知道怎么得到库名,当我们自已要用到一个第三方提供的库名字libtest.so,那么我们只要把libtest.so拷贝到/usr/lib里,编译时加上-ltest参数,我们就能用上libtest.so库了(当然要用libtest.so库里的函数,我们还需要与libtest.so配套的头文件)

放在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接了,但如果库文件没放在这三个目录里,而是放在其他目录里,这时我们只用-l参数的话,链接还是会出错,出错信息大概是:“/usr/bin/ld: cannot find -lxxx”,也就是链接程序ld在那3个目录里找不到libxxx.so,这时另外一个参数-L就派上用场了,比如常用的X11的库,它在/usr/X11R6/lib目录下,我们编译时就要用-L/usr/X11R6/lib -lX11参数,-L参数跟着的是库文件所在的目录名。再比如我们把libtest.so放在/aaa/bbb/ccc目录下,那链接参数就是-L/aaa/bbb/ccc -ltest

另外,大部分libxxxx.so只是一个链接,以RH9为例,比如libm.so它链接到/lib/libm.s
o.x,/lib/libm.so.6又链接到/lib/libm-2.3.2.so,

如果没有这样的链接,还是会出错,因为ld只会找libxxxx.so,所以如果你要用到xxxx
库,而只有libxxxx.so.x或者libxxxx-x.x.x.so,做一个链接就可以了
ln -s libxxxx-x.x.x.so libxxxx.so

手工来写链接参数总是很麻烦的,还好很多库开发包提供了生成链接参数的程序,名字一般叫xxxx-config,一般放在/usr/bin目录下,比如

gtk1.2的链接参数生成程序是gtk-config,执行gtk-config --libs就能得到以下输出"-
L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic

-lgmodule -lglib -ldl -lXi -lXext -lX11 -lm",这就是编译一个gtk1.2程序所需的g
tk链接参数,xxx-config除了--libs参数外还有一个参数是--cflags用来生成头文
件包含目录的,也就是-I参数,在下面我们将会讲到。你可以试试执行gtk-config 
--libs --cflags,看看输出结果

现在的问题就是怎样用这些输出结果了,最笨的方法就是复制粘贴或者照抄,聪明的办
法是在编译命令行里加入这个`xxxx-config --libs --cflags`,比如编译一个gtk程序:gcc gtktest.c `gtk-config --libs --cflags`这样
就差不多了。注意`不是单引号,而是1键左边那个键。

除了xxx-config以外,现在新的开发包一般都用pkg-config来生成链接参数,使用方法
跟xxx-config类似,但xxx-config是针对特定的开发包,但pkg-config包含很多开发包的链接参数的生成,用pkg-config --list-all命令可以列出所支持的所有开发包,pkg-config的用法就是pkg -config pagName --libs --cflags,其中pagName是包名,是pkg-config--list-all里列出名单中的一个,比如gtk1.2的名字就是gtk+,pkg-

config gtk+ --libs --cflags的作用跟gtk-config --libs --cflags是一样的。比如:
gcc gtktest.c `pkg-config gtk+ --libs --cflags`


5、-include和-I参数

-include用来包含头文件,但一般情况下包含头文件都在源码里用#include xxxxxx实现,-include参数很少用。-I参数是用来指定头文件目录,/usr/include目录一般是不用指定的,gcc知道去那里找,但是如果头文件不在/usr/include里我们就要用-I参数指定了,比如头文件放在/myinclude目录里,那编译命令行就要加上-I/myinclude参数了,如果不加你会得到一个"xxxx.h: No such file or directory"的错误。-I参数可以用相对路径,比如头文件在当前目录,可以用-I.来指定。上面我们提到的--cflags参数就是用来生成-I参数的

6、-O参数

这是一个程序优化参数,一般用-O2就是,用来优化程序用的,比如gcc test.c -O2,优化得到的程序比没优化的要小,执行速度可能也有所提高

7、-shared参数
编译动态库时要用到,比如gcc -shared test.c -o libtest.so

8、几个相关的环境变量
PKG_CONFIG_PATH:用来指定pkg-config用到的pc文件的路径,默认是/usr/lib/pkgconf
ig,pc文件是文本文件,扩展名是.pc,里面定义开发包的安装路径,Libs参数和Cflags参数等等。
CC:用来指定c编译器
CXX:用来指定cxx编译器
LIBS:跟上面的--libs作用差不多
CFLAGS:跟上面的--cflags作用差不多
CC,CXX,LIBS,CFLAGS手动编译时一般用不上,在做configure时有时用到,一般情况
下不用管
环境变量设定方法:export ENV_NAME=xxxxxxxxxxxxxxxxx

9、关于交叉编译

交叉编译通俗地讲就是在一种平台上编译出能运行在体系结构不同的另一种平台上,比
如在我们地PC平台(X86 CPU)上编译出能运行在sparc

CPU平台上的程序,编译得到的程序在X86 CPU平台上是不能运行的,必须放到sparc CPU平台上才能运行。当然两个平台用的都是linux

这种方法在异平台移植和嵌入式开发时用得非常普遍

相对与交叉编译,我们平常做的编译就叫本地编译,也就是在当前平台编译,编译得到
的程序也是在本地执行

用来编译这种程序的编译器就叫交叉编译器,相对来说,用来做本地编译的就叫本地编
译器,一般用的都是gcc,但这种gcc跟本地的gcc编译器

是不一样的,需要在编译gcc时用特定的configure参数才能得到支持交叉编译的gcc

为了不跟本地编译器混淆,交叉编译器的名字一般都有前缀,比如sparc-xxxx-linux-gn
u-gcc,sparc-xxxx-linux-gnu-g++ 等等

10、交叉编译器的使用方法
使用方法跟本地的gcc差不多,但有一点特殊的是:必须用-L和-I参数指定编译器用spar
c系统的库和头文件,不能用本地(X86)的库(头文件有时可以用本地的)

例子:
sparc-xxxx-linux-gnu-gcc test.c -L/path/to/sparcLib -I/path/to/sparcInclude


 

gcc与g++
Linux 中最重要的软件开发工具是 GCC。GCC 是 GNU 的 C 和 C++ 编译器。实际上,GCC 能够编译三种语言:C、C++ 和 Object C(C 语言的一种面向对象扩展)。利用 gcc 命令可同时编译并连接 C 和 C++ 源程序。
GCC 可同时用来编译 C 程序和 C++ 程序。一般来说,C 编译器通过源文件的后缀名来判断是 C 程序还是 C++ 程序。在 Linux 中,C 源文件的后缀名为 .c,而 C++ 源文件的后缀名为 .C 或 .cpp。
gcc 命令只能编译 C++ 源文件,而不能自动和 C++ 程序使用的库连接。因此,通常使用 g++ 命令来完成 C++ 程序的编译和连接,该程序会自动调用 gcc 实现编译。
选项 解释
-ansi 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色,
例如 asm 或 typeof 关键词。
-c 只编译并生成目标文件。
-DMACRO 以字符串“1”定义 MACRO 宏。
-DMACRO=DEFN 以字符串“DEFN”定义 MACRO 宏。
-E 只运行 C 预编译器。
-g 生成调试信息。GNU 调试器可利用该信息。
-IDIRECTORY 指定额外的头文件搜索路径DIRECTORY。
-LDIRECTORY 指定额外的函数库搜索路径DIRECTORY。
-lLIBRARY 连接时搜索指定的函数库LIBRARY。
-m486 针对 486 进行代码优化。
-o FILE 生成指定的输出文件。用在生成可执行文件时。
-O0 不进行优化处理。
-O 或 -O1 优化生成代码。
-O2 进一步优化。
-O3 比 -O2 更进一步优化,包括 inline 函数。
-shared 生成共享目标文件。通常用在建立共享库时。
-static 禁止使用共享连接。
-UMACRO 取消对 MACRO 宏的定义。
-w 不生成任何警告信息。
-Wall 生成所有警告信息。
例子:

GCC编译OpengL: gcc hello.c -o hello -L/usr/X11R6/lib/ -lGL -lGLU -lglut

11、基本用法

一般用作C语言编译器时是gcc,而用作C++语言编译器时是g++。
其语法结构为:

       gcc [-c|-S|-E] [-std=standard]
           [-g] [-pg] [-Olevel]
           [-Wwarn...] [-pedantic]
           [-Idir...] [-Ldir...]
           [-Dmacro[=defn]...] [-Umacro]
           [-foption...] [-mmachine-option...]
           [-o outfile] infile...
虽然看起来选项非常多,好象挺高深的。但只有一个infile是必需选项,故我们初学者只需要知道这样的形式就行了:
gcc source-files
即用任何一种文本编译器编写C语言源程序,保存为一个UNIX下格式的文本文件,比如文件名为test.c,那么用下面这个命令把源程序变成可执行程序就行了:
gcc test.c
如果你是用Windows下的文件编辑软件写的源程序,在用gcc编译之前最好用命令dos2unix转换一下文件格式。因为早期的gcc版本不能识别windows下的文件格式。会报奇怪的错误。
dos2unix命令由软件包tofrodos提供,软件包信息如下:
Description: Converts DOS <-> Unix text files, alias tofromdos
 DOS text files traditionally have CR/LF (carriage return/line feed) pairs
 as their new line delimiters while Unix text files traditionally have
 LFs (line feeds) to terminate each line.
 .
 Tofrodos comprises one program, "fromdos" alias "todos", which converts
 text files to and from these formats. Use "fromdos" to convert DOS
 text files to the Unix format, and "todos" to convert Unix text files
 to the DOS format.
 .
 This functionality is also available via the dos2unix/unix2dos symlinks.
 .
  Homepage: http://www.thefreecountry.com/tofrodos/index.shtml
即实现Windows文本文件格式和Linux文本文件格式的转换。
因为Windows下文本文件以CR和LF行结束符,而Linux下文本文件以LF作为行结束符。所以在Linux下编写的文本文件到Windows下用记事本等软件打开时会出现所有内容挤在一行的现象。
用C语言的习惯来说就是:Windows下文本文件行结束符是'\r'和'\n'而Linux下是'\n',即Windows下是十六进制的0x0d和0x0a,而Linux下是0x0a
用上面的编译命令:
gcc test.c
将在当前目录下产生一个可执行程序a.out,要运行程序试试?输入命令:
./a.out
如果想要产生一个自己命令的程序,即程序名不是默认的a.out,可以用这种命令格式:
gcc -o out-file source-file
比如:
gcc -o myprog test.c
将到源程序test.c变成一个可执行程序myprog,运行这个命令看看你的成果吧:
./myprog

12、编译多个文件
如果你的程序源代码比较多,并且分在几个源文件里面,互相之前有调用现象,就要用gcc编译多个源文件了。命令格式如下:
gcc -o out-file source-file-a source-file-b source-file-c ...
可以把所有源代码文件联合编译生成一个程序out-file

13、代码风格
很多人都会讲代码风格问题,但这应该是机器做的事而不是人做的事!
Linux下有一个很好的软件indent可以用来把代码按照一定的风格进行整理。该软件包信息如下:
Description: C language source code formatting program
 The `indent' program changes the appearance of a C program by
 inserting or deleting whitespace.
 .
 `indent' also provides options for controlling the alignment of braces and
 declarations, program indenting, and other stylistic parameters, including
 formatting of both C and C++ comments.
比如我们编写了一个源程序test.c,代码格式比较乱,如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
        int port = 0;
    printf("program begin\n");
    if (argc != 3) {
        printf("程序参数个数不对!正确输入方法如下:\n%s ip-address port\n",argv[0]);
exit(0);
        }
    if(atoi(argv[2]) <0 || atoi(argv[2]) > 65535) {printf("端口输入不对!应该是0-65535的值。程序取默认值3456\n"); port = 3456;}
    else port = atoi(argv[2]);
    printf("命令如下:%s %s %d\n", argv[0], argv[1], port);
    return 0;
}
用indent命令格式化一下,命令如下:
indent test.c
源代码变成下面的样子了:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int
main (int argc, char **argv)
{
  int port = 0;
  printf ("program begin\n");
  if (argc != 3)
    {
      printf
        ("程序参数个数不对!正确输入方法如下:\n%s ip-address port\n",
         argv[0]);
      exit (0);
    }
  if (atoi (argv[2]) < 0 || atoi (argv[2]) > 65535)
    {
      printf
        ("端口输入不对!应该是0-65535的值。程序取默认值3456\n");
      port = 3456;
    }
  else
    port = atoi (argv[2]);
  printf ("命令如下:%s %s %d\n", argv[0],argv[1], port);
  return 0;
}
这种代码风格是GNU的风格,是indent的默认格式。
但我们多数写程序的人可能都是学的Kernighan & Ritchie代码风格,用如下命令格式化:
indent -kr test.c
代码格式化成这样了:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    int port = 0;
    printf("program begin\n");
    if (argc != 3) {
        printf
            ("程序参数个数不对!正确输入方法如下:\n%s ip-address port\n",
             argv[0]);
        exit(0);
    }
    if (atoi(argv[2]) < 0 || atoi(argv[2]) > 65535) {
        printf
            ("端口输入不对!应该是0-65535的值。程序取默认值3456\n");
        port = 3456;
    } else
        port = atoi(argv[2]);
    printf("命令如下:%s %s %d\n", argv[0], argv[1], port);
    return 0;
}
记住:本该机器来做的事,不要手工去做!
当然,如果你用indent格式化时报错或格式化后发现不对劲,一定是你的代码里有问题,比如少了一个“}“

14、养成好的习惯
大家都写C程序,可有人的代码总会有问题,而写得好一点的就会基本没错误。
这需要一个慢慢练习的过程,但作为初学者,你在编译程序时就加上gcc的-Wall选项吧。即命令格式为:
gcc -Wall -o out-file source-file
这样,程序中每行代码有问题都有提到提示。初学者把每个Warning和Error都弄清楚对养成好的编程习惯是相当有用的。

15、为调试程序作准备
可能我们的程序编译通过了,也生成了可执行程序,但可能运行的时候出错,甚至刚开始运行一段时间都没问题后来某一天出了问题。
有些问题一看错误提示就明白了。但有些可能花较长时间都想不明白,只能调试程序了。
为了保证可以调试程序,我们在用gcc编译程序时要加上-g选项,即命令格式如下:
gcc -Wall -g -o out-file source-file
这个命令就会把源代码编译产生的可执行程序里加上调试信息,如果程序出错出现Core dump文件时可以从Core dump文件里快速定位到程序哪行出了问题。