linux 指令之 g++

2015年11月6日 16:57:34

最全的学习材料当然是 GCC 的官方文档。如果有耐心但是英语不好的话,可以参看一下 这个网站。先说

gcc/g++ 执行的四个过程:

  1. gcc -E 仅作预处理,即只激活预处理,不进行编译、汇编和链接 {通常以什么为后缀?}

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

  2. gcc -s 编译到汇编语言,不进行汇编和链接,即只激活预处理和编译,生成汇编语言

    $gcc -S main.i -o main.s #参数-S,大写

  3. gcc -c 编译、汇编到目标代码,不进行链接,即只激活预处理、编译和汇编功能,生成目标文件(.o)

    $gcc -c main.s -o main.o

  4. 生成,到执行文件 (根据依赖关系链接各目标文件,生成最终的执行程序)

    $gcc main.o printf1.o printf2.o -o main

更详细的描述参考:Linux GCC常用命令gcc/g++执行的步骤及参数简介

了解 g++ 的编译流程之后,我们说一下

g++ 和 gcc 的区别

  1. 后缀为 .c 的,gcc 把它当作是 C 程序,而 g++ 当作是 c++ 程序;后缀为 .cpp 的,两者都会认为是 c++ 程序。
  2. 编译阶段,g++ 会调用 gcc,对于 c++ 代码,两者是等价的;(生成阶段)但是因为 gcc 命令不能自动和 C++ 程序使用的库联接,所以通常用 g++ 来完成链接,为了统一起见,干脆编译/链接统统用 g++ 了。其实 gcc 编译 C++ 程序 也可以。

进一步的区别参考:gcc和g++的区别

然后说一下

-I(大写的 i)、-L 这两个参数

  • -I 后跟路径名,指定 gcc 编译时的头文件搜索路径;(+系统默认的路径(具体呢?)下查找)
  • -L 后跟路径名,指定 gcc 链接时查找库的搜索路径。(+系统默认的路径下查找)

进一步延伸一下,-L 指定了库的路径,但具体是哪一个 .so 文件是怎么确定呢?

-l (小写的 L)参数

下面通过记录具体的调试过程,逐步介绍。

  1. > g++ main.cpp -o main

    1
    2
    3
    4
    5
    nl@linux-zds2:~/nielong/curlTest/1getAndpost> g++ main.cpp -o main
    main.cpp:2:23: fatal error: curl/curl.h: 没有那个文件或目录
    #include <curl/curl.h>
    ^
    compilation terminated.

    因为 #include <curl/curl.h>,而且在系统指定目录中找不到此文件。所以报以上错误。

    使用 -I 参数指定到上一级目录中查找头文件。(将 curl 文件夹拷贝到了上一级目录)

  2. > g++ main.cpp -o main -I ../

    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
    nl@linux-zds2:~/nielong/curlTest/1getAndpost> g++ main.cpp -o main -I ../
    main.cpp: In function ‘int main()’:
    main.cpp:53:28: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
    getUrl("/tmp/get0.html");
    ^
    main.cpp:54:30: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
    postUrl("/tmp/post0.html");
    ^
    /tmp/cc3fVgxs.o:在函数‘getUrl(char*)’中:
    main.cpp:(.text+0x4c):对‘curl_slist_append’未定义的引用
    main.cpp:(.text+0x55):对‘curl_easy_init’未定义的引用
    main.cpp:(.text+0x7e):对‘curl_easy_setopt’未定义的引用
    main.cpp:(.text+0x99):对‘curl_easy_setopt’未定义的引用
    main.cpp:(.text+0xb3):对‘curl_easy_setopt’未定义的引用
    main.cpp:(.text+0xcd):对‘curl_easy_setopt’未定义的引用
    main.cpp:(.text+0xd9):对‘curl_easy_perform’未定义的引用
    main.cpp:(.text+0xee):对‘curl_slist_free_all’未定义的引用
    main.cpp:(.text+0xfa):对‘curl_easy_cleanup’未定义的引用
    /tmp/cc3fVgxs.o:在函数‘postUrl(char*)’中:
    main.cpp:(.text+0x14b):对‘curl_easy_init’未定义的引用
    main.cpp:(.text+0x175):对‘curl_easy_setopt’未定义的引用
    main.cpp:(.text+0x190):对‘curl_easy_setopt’未定义的引用
    main.cpp:(.text+0x1ab):对‘curl_easy_setopt’未定义的引用
    main.cpp:(.text+0x1c5):对‘curl_easy_setopt’未定义的引用
    main.cpp:(.text+0x1d1):对‘curl_easy_perform’未定义的引用
    main.cpp:(.text+0x1e0):对‘curl_easy_cleanup’未定义的引用
    collect2: error: ld returned 1 exit status

    报以上错误是因为动态链接库未指定,所以相关方法未定义。使用 -l(小写 L)指定 libcurl.so

    使用 -l curl 参数链接 .so 文件。(-l curl 会相对应的使用 libcurl.so 链接库,具体因果在后面详述)

  3. > g++ main.cpp -o main -I ../ -lcurl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    nl@linux-zds2:~/nielong/curlTest/1getAndpost> g++ main.cpp -o main -I ../ -lcurl
    main.cpp: In function ‘int main()’:
    main.cpp:53:28: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
    getUrl("/tmp/get0.html");
    ^
    main.cpp:54:30: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
    postUrl("/tmp/post0.html");
    ^
    /usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: cannot find -lcurl
    collect2: error: ld returned 1 exit status

    报错找不打 -lcurl 指定的 .so 文件,此为搜索路径问题。关于 解决/usr/bin/ld: cannot find -lxxx 问题

    使用 -L 参数指定到上一级目录中查找 .so 文件。(在上一级目录创建了 usr/lib/libcurl.so.4.* 的软链接,软连接 & 硬链接 在后面详述)

    另外,使用 -l xxx 命令有一个细节,此命令需要放在 main.cpp 的后面。具体参照:AB

  4. > g++ main.cpp -o main -I ../ -lcurl -L ../

    1
    2
    3
    4
    5
    6
    7
    8
    9
    nl@linux-zds2:~/nielong/curlTest/1getAndpost> g++ main.cpp -o main -I ../ -lcurl -L ../                                               
    main.cpp: In function ‘int main()’:
    main.cpp:53:28: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
    getUrl("/tmp/get0.html");
    ^
    main.cpp:54:30: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
    postUrl("/tmp/post0.html");
    ^
    nl@linux-zds2:~/nielong/curlTest/1getAndpost>

    搞定。

-l crul 怎么对应的 libcurl.so?

参考 链接库。经过上面的介绍,再

强调两点:

  1. 预处理阶段处理头文件。所以,使用 -I 指定头文件路径时,仅仅作用在预处理阶段,在其后的编译、汇编、链接阶段不再需要 -I 指定头文件路径,因为经过预处理操作,预处理的结果中已包含各头文件。
  2. 链接阶段链接库。即,使用 -L 指定库路径时,作用于链接阶段,在预处理、编译、汇编阶段尚不需要 -L 指定库路径。

附录:gcc/g++常用的参数

-IDir
指定额外的头文件搜索目录,编译器首先在Dir中寻找,然后按照常规的顺序搜索头文件。
-include file
相当于在源程序中添加头文件,相当于#include “file”。
-LDir
指定编译的时候编译器搜索的库路径。
-lLIBRARY
指定编译的时候使用的库。


-E
只激活预处理功能。
-S
只激活预编译和编译功能。
-c
只激活预处理、编译和汇编功能。


-shared
生成共享目标文件,通常用于建立共享库时使用。
-static
禁止使用共享库。


-w
不生成任何警告信息。
-Wall
生成所有警告信息。使用它能够使GCC产生尽可能多的警告信息。并非全部。参考 GCC常用选项&使用详解
-Werror
在发生警报时取消编译操作,即把报警当作是错误。
-C
在预处理的时候不删除注释信息,一般和-E联合使用,用于分析程序。
-M
生成文件的关联信息,包含目标文件生成所依赖的所有源代码。
-MM
和上面的一样,但是忽略头文件造成的依赖关系。
-MD、-MMD
对应和-M、-MM相同,只不过将输出输入到.d文件中去。

-D
一般Makefie里的 -DHAVE_CONFIG_H是作为CFLAGS参数传给gcc的。

1
2
gcc [-c|-S|-E] [-std=standard]
[-Dmacro[=defn]...] [-Umacro]

查man手册知,就是通过 gcc -DHAVE_CONFIG , 定义了 HAVE_CONFIG_H 这个宏。
随手找了个bash的源码,截一段: ./lib/tilde/tilde.c bash源码中的源文件~~

1
2
3
#if defined (HAVE_CONFIG_H)
# include <config.h>
#endif

而这里的 config.h 文件,是通过 configure 生成的,里面关于编译环境的一些宏。

通过 configure 检查出来的这些宏,在做跨平台时使用非常方便。

GCC高级编译链接参数

  1. 部分动态链接部分静态链接
  2. 设置动态链接 PATH

两者都涉及到 -wl 参数,关于其使用的 注意事项