2015年11月6日 16:57:34
最全的学习材料当然是 GCC 的官方文档。如果有耐心但是英语不好的话,可以参看一下 这个网站。先说
gcc/g++ 执行的四个过程:
gcc -E
仅作预处理,即只激活预处理,不进行编译、汇编和链接 {通常以什么为后缀?}$gcc -E main.c -o main.i
gcc -s
编译到汇编语言,不进行汇编和链接,即只激活预处理和编译,生成汇编语言$gcc -S main.i -o main.s #参数-S,大写
gcc -c
编译、汇编到目标代码,不进行链接,即只激活预处理、编译和汇编功能,生成目标文件(.o)$gcc -c main.s -o main.o
生成,到执行文件 (根据依赖关系链接各目标文件,生成最终的执行程序)
$gcc main.o printf1.o printf2.o -o main
更详细的描述参考:Linux GCC常用命令、gcc/g++执行的步骤及参数简介
了解 g++ 的编译流程之后,我们说一下
g++ 和 gcc 的区别
- 后缀为 .c 的,gcc 把它当作是 C 程序,而 g++ 当作是 c++ 程序;后缀为 .cpp 的,两者都会认为是 c++ 程序。
- 编译阶段,g++ 会调用 gcc,对于 c++ 代码,两者是等价的;(生成阶段)但是因为 gcc 命令不能自动和 C++ 程序使用的库联接,所以通常用 g++ 来完成链接,为了统一起见,干脆编译/链接统统用 g++ 了。其实 gcc 编译 C++ 程序 也可以。
进一步的区别参考:gcc和g++的区别
然后说一下
-I(大写的 i)、-L 这两个参数
-I
后跟路径名,指定 gcc 编译时的头文件搜索路径;(+系统默认的路径(具体呢?)下查找)-L
后跟路径名,指定 gcc 链接时查找库的搜索路径。(+系统默认的路径下查找)
进一步延伸一下,-L
指定了库的路径,但具体是哪一个 .so 文件是怎么确定呢?
-l (小写的 L)参数
下面通过记录具体的调试过程,逐步介绍。
> g++ main.cpp -o main
1
2
3
4
5nl@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 文件夹拷贝到了上一级目录)> 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
27nl@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 链接库,具体因果在后面详述)> g++ main.cpp -o main -I ../ -lcurl
1
2
3
4
5
6
7
8
9
10nl@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.* 的软链接,软连接 & 硬链接 在后面详述)> g++ main.cpp -o main -I ../ -lcurl -L ../
1
2
3
4
5
6
7
8
9nl@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?
参考 链接库。经过上面的介绍,再
强调两点:
- 预处理阶段处理头文件。所以,使用
-I
指定头文件路径时,仅仅作用在预处理阶段,在其后的编译、汇编、链接阶段不再需要-I
指定头文件路径,因为经过预处理操作,预处理的结果中已包含各头文件。 - 链接阶段链接库。即,使用
-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 | gcc [-c|-S|-E] [-std=standard] |
查man手册知,就是通过 gcc -DHAVE_CONFIG , 定义了 HAVE_CONFIG_H 这个宏。
随手找了个bash的源码,截一段: ./lib/tilde/tilde.c bash源码中的源文件~~
1 | #if defined (HAVE_CONFIG_H) |
而这里的 config.h 文件,是通过 configure 生成的,里面关于编译环境的一些宏。
通过 configure 检查出来的这些宏,在做跨平台时使用非常方便。
GCC高级编译链接参数
- 部分动态链接部分静态链接
- 设置动态链接 PATH
两者都涉及到 -wl
参数,关于其使用的 注意事项。