“-Wall”选项打开所有最常用到的编译警告,强烈建议打开,可以捕捉到许多在C编程中最常发生的错误。
“-o”选项来为可执行文件指定一个不同的输出文件。
“-c”用于把源码文件编译成对象文件。
对象文件包含的是机器码,其中任何对在其他文件中的函数(或变量)的内存地址的引用都留着没有被解析。这样就允许在互相之间不直接引用的情况下编译各个源代码文件。链接器在生成可执行文件时会填写这些还缺少的地址,然后把所有的对象文件组合在一起生成单个的可执行文件。当用“-c”来编译时,编译器会自动生成与源文件同名,但用“.o”来代替原来的扩展名的对象文件。
gcc使用链接器ld来施行链接,它是一个单独的程序。在GNU系统上用到的是GNU的链接器,即GNU ld。
通常,链接要快于编译----在一个有许多源文件的大型项目中,只重新编译那些被修改过的文件可以显著地节省时间。仅仅重编译项目中修改过的文件的过程可以用GNU Make来自动完成。
标准的系统库通常能在“/usr/lib”和“/lib”目录下,C标准库自身存放在“/usr/lib/libc.a”中,包含ANSI/ISO C标准指定的各个函数。其他库都需要显示或隐示指定。
“-lNAME”试图链接标准库目录下的文件名为“libNAME.a”中的对象文件。在大型程序中通常会用到很多“-l”选项,来链接象数学库,图形库和网络库。使用选项“-lNAME”的情况下,静态库“libNAME”可以用于链接,但编译器首先会检查具有相同名字和“.so”为扩展名的共享库。默认情况下,载入器仅在一些预定义的系统目录中查找共享库,比如“/usr/local/lib”和“/usr/lib”。如果库不在这些目录中,那它必须被添加到载入路径(load path)中去。设置载入路径的最简单方法是通过环境变量LD_LIBRARY_PATH。
“-static”选项可以迫使gcc静态链接,避免使用共享库。
使用库文件,为了得到函数参数和返回值正确类型的声明,必须包括入相应的头文件。如果没有函数声明,可能传递错误类型的函数参数,从而导致不对的结果。
默认情况下,gcc在下面目录中搜索头文件:
/usr/local/include/
/usr/include/
在下面目录中搜索库:
/usr/local/lib/
/usr/lib/
“-I”用于把新目录添加到include路径上。例如,-I/opt/gdbm-1.8.3/include,注意,不应该在源代码中的#include语句中放入头文件的绝对路径,因为这会让该程序不能在其他系统上编译。“-I”选项或下面就要介绍的INCLUDE_PATH变量用来设置头文件的include路径,一般IDE都有设置包含include路径的选项。
“-L”用于把新目录添加到库搜索路径上。例如,-L/opt/gdbm-1.8.3/lib。
“-ansi”禁止那些与ANSI/ISO标准冲突的GNU扩展特性。
“-std”选项来控制GCC编译时采用的某个C语言标准。
‘-W’这是一个类似“-Wall”的通用选项,它对a selection of常见编程错误产生警告。“-W”和“-Wall”选项通常同时使用。
“-DNAME”选项在命令行上定义预处理宏NAME,默认情况下,其值为1。“-D”命令行选项可以用来定义有值的宏,形式是“-DNAME=VALUE”,例如-DNUM="2+2",预处理器将把NUM替换成2+2。当宏是某个表达式的一部分时,用圆括号把宏括起来是个好主意,比如10 * (NUM)
宏通常都是未定义的除非用“-D”选项在命令行上指定,或在源文件中(或库的头文件)用#define定义。有些宏是由编译器自动定义的----这些宏会用到由双下划线(__)开始的保留的名字空间。预定义的宏的完整列表可以这样得到,对某个空文件运行带“-dM”选项的GNU预处理器cpp:
[root@iZ23i5mx5vxZ ~]# cpp -dM /dev/null #define __DBL_MIN_EXP__ (-1021) #define __FLT_MIN__ 1.17549435e-38F #define __CHAR_BIT__ 8 #define __WCHAR_MAX__ 2147483647 #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1 #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1 #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1 #define __DBL_DENORM_MIN__ 4.9406564584124654e-324 #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1 #define __FLT_EVAL_METHOD__ 0 #define __unix__ 1 #define __x86_64 1 #define __DBL_MIN_10_EXP__ (-307) #define __FINITE_MATH_ONLY__ 0 #define __GNUC_PATCHLEVEL__ 7 #define __DEC64_MAX_EXP__ 385 #define __SHRT_MAX__ 32767 #define __LDBL_MAX__ 1.18973149535723176502e+4932L #define __UINTMAX_TYPE__ long unsigned int #define __linux 1 #define __DEC32_EPSILON__ 1E-6DF #define __unix 1 #define __LDBL_MAX_EXP__ 16384 #define __linux__ 1 #define __SCHAR_MAX__ 127 #define __DBL_DIG__ 15 #define __SIZEOF_INT__ 4 #define __SIZEOF_POINTER__ 8 #define __USER_LABEL_PREFIX__ #define __STDC_HOSTED__ 1 #define __LDBL_HAS_INFINITY__ 1 #define __FLT_EPSILON__ 1.19209290e-7F #define __LDBL_MIN__ 3.36210314311209350626e-4932L #define __DEC32_MAX__ 9.999999E96DF #define __SIZEOF_LONG__ 8 #define __DECIMAL_DIG__ 21 #define __gnu_linux__ 1 #define __LDBL_HAS_QUIET_NAN__ 1 #define __GNUC__ 4 #define __MMX__ 1 #define __FLT_HAS_DENORM__ 1 #define __SIZEOF_LONG_DOUBLE__ 16 #define __BIGGEST_ALIGNMENT__ 16 #define __DBL_MAX__ 1.7976931348623157e+308 #define __DBL_HAS_INFINITY__ 1 #define __DEC32_MIN_EXP__ (-94) #define __LDBL_HAS_DENORM__ 1 #define __DEC128_MAX__ 9.999999999999999999999999999999999E6144DL #define __DEC32_MIN__ 1E-95DF #define __DBL_MAX_EXP__ 1024 #define __DEC128_EPSILON__ 1E-33DL #define __SSE2_MATH__ 1 #define __amd64 1 #define __LONG_LONG_MAX__ 9223372036854775807LL #define __SIZEOF_SIZE_T__ 8 #define __SIZEOF_WINT_T__ 4 #define __GCC_HAVE_DWARF2_CFI_ASM 1 #define __GXX_ABI_VERSION 1002 #define __FLT_MIN_EXP__ (-125) #define __DBL_MIN__ 2.2250738585072014e-308 #define __LP64__ 1 #define __DECIMAL_BID_FORMAT__ 1 #define __DEC128_MIN__ 1E-6143DL #define __REGISTER_PREFIX__ #define __DBL_HAS_DENORM__ 1 #define __NO_INLINE__ 1 #define __FLT_MANT_DIG__ 24 #define __VERSION__ "4.4.7 20120313 (Red Hat 4.4.7-17)" #define __DEC64_EPSILON__ 1E-15DD #define __DEC128_MIN_EXP__ (-6142) #define unix 1 #define __SIZE_TYPE__ long unsigned int #define __ELF__ 1 #define __FLT_RADIX__ 2 #define __LDBL_EPSILON__ 1.08420217248550443401e-19L #define __GNUC_RH_RELEASE__ 17 #define __SSE_MATH__ 1 #define __k8 1 #define __SIZEOF_PTRDIFF_T__ 8 #define __x86_64__ 1 #define __DEC32_SUBNORMAL_MIN__ 0.000001E-95DF #define __FLT_HAS_QUIET_NAN__ 1 #define __FLT_MAX_10_EXP__ 38 #define __LONG_MAX__ 9223372036854775807L #define __DEC128_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143DL #define __FLT_HAS_INFINITY__ 1 #define __DEC64_MAX__ 9.999999999999999E384DD #define __CHAR16_TYPE__ short unsigned int #define __DEC64_MANT_DIG__ 16 #define __DEC32_MAX_EXP__ 97 #define linux 1 #define __SSE2__ 1 #define __LDBL_MANT_DIG__ 64 #define __DBL_HAS_QUIET_NAN__ 1 #define __k8__ 1 #define __WCHAR_TYPE__ int #define __SIZEOF_FLOAT__ 4 #define __DEC64_MIN_EXP__ (-382) #define __FLT_DIG__ 6 #define __INT_MAX__ 2147483647 #define __amd64__ 1 #define __FLT_MAX_EXP__ 128 #define __DBL_MANT_DIG__ 53 #define __DEC64_MIN__ 1E-383DD #define __WINT_TYPE__ unsigned int #define __SIZEOF_SHORT__ 2 #define __SSE__ 1 #define __LDBL_MIN_EXP__ (-16381) #define __LDBL_MAX_10_EXP__ 4932 #define __DBL_EPSILON__ 2.2204460492503131e-16 #define _LP64 1 #define __SIZEOF_WCHAR_T__ 4 #define __DEC_EVAL_METHOD__ 2 #define __INTMAX_MAX__ 9223372036854775807L #define __FLT_DENORM_MIN__ 1.40129846e-45F #define __CHAR32_TYPE__ unsigned int #define __FLT_MAX__ 3.40282347e+38F #define __SIZEOF_DOUBLE__ 8 #define __FLT_MIN_10_EXP__ (-37) #define __INTMAX_TYPE__ long int #define __DEC128_MAX_EXP__ 6145 #define __GNUC_MINOR__ 4 #define __DEC32_MANT_DIG__ 7 #define __DBL_MAX_10_EXP__ 308 #define __LDBL_DENORM_MIN__ 3.64519953188247460253e-4951L #define __STDC__ 1 #define __PTRDIFF_TYPE__ long int #define __DEC64_SUBNORMAL_MIN__ 0.000000000000001E-383DD #define __DEC128_MANT_DIG__ 34 #define __LDBL_MIN_10_EXP__ (-4931) #define __SIZEOF_LONG_LONG__ 8 #define __LDBL_DIG__ 18 #define __GNUC_GNU_INLINE__ 1
“-E”选项导致gcc只运行预处理器,显示展开后的输出后,没有编译预处理过的源代码就退出。
“-g”调试选项来在对象文件和可执行文件中存储另外的调试信息。这些调试信息可以使得在追踪错误时能从特定的机器码指令对应到源代码文件中的行。它也可以使得该程序能被象gdb之类的调试器追踪调试。调试器是通过把函数名和变量(和所有对它们的引用),以及它们相应的原代码的行号存储到对象文件和可执行文件的符号表中来工作的。
“-g”选项除了允许程序在调试器控制下运行以外,另一个有用的应用是找到程序崩溃的环境。
当一个程序异常退出时,操作系统会写一个常规被称为“core”的文件,它包括了程序在崩溃刹那间的内存状态。结合由“-g”选项生成的符号表中的信息,程序员从core文件中可以查询到程序在哪一行停止了,和在那时候的变量的值。这在软件开发期间和部署以后都是很有用的----它允许当程序在“某处”崩溃时能对问题展开调查。例如:
Segmentation fault (core dumped)
只要显示了报错信息“core dumped”,操作系统就在当前目录下生成了一个名为“core”的文件。该core文件包含程序在被终结时用到的内存页面的完整备份。由于core文件可能很大并且可能快速地填满系统中的可用磁盘空间,一些系统被配置成在默认情况下不写core文件。在GNU Bash shell中,命令ulimit –c可以控制设定core文件的最大值。如果这个限定值是零,则不会生成core文件。输入下面的命令可以显示当前的限定值:
[root@iZ23i5mx5vxZ ~]# ulimit -a
core file size (blocks, -c) 0
ulimit -c unlimited 可设置允许的core文件大小。
GNU调试器gdb可以用下面的命令载入core文件:
$ gdb EXECUTABLE-FILE CORE-FILE
注意,原来的可执行文件和core文件都要提供,以备调试----没有相应的可执行文件,只有core文件是不能调试的。
注:用gdb调试进程时最烦琐的就是如何继续跟进fork出的子进程。GDB 7.0支持调试子进程,参考https://blog.csdn.net/pbymw8iwm/article/details/7876797
例如:
$ gdb a.out core
调试器马上打印出诊断信息和显示程序崩溃处的代码行(第13行):
$ gdb a.out core
Core was generated by ‘./a.out’.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 0x080483ed in a (p=0x0) at null.c:13
13 int y = *p;
(gdb)
“-OLEVEL”用来选择哪一种优化级别,这里LEVEL是从1到3的数字。“-O0”或没有“-O”选项(默认),在该优化级别,GCC不会实施任何优化。
“-O1”或“-O”这一级会打开那些不需要任何速度-空间折衷的最常见形式的优化。带选项“-O1”编译所花时间可能常常少于带“-O0”编译,这是由于在简单优化后减少了需要处理的数据量。
“-O2”该选项除了“-O1”用到的那些优化以外,打开进一步优化。对要部署的程序而言,该选项通常是最佳选择,因为在不增加可执行文件大小的情况下,它提供了最大的优化。它是各种GNU软件发行包的默认优化级别。
就绝大部分目的而言,调试时用“-O0”,开发和部署时用“-O2”就足够了。
在GCC下,可以组合使用优化与调试选项“-g”,而许多其他编译器不支持这样做。当程序出人意料地崩溃时,有点调试信息总比什么都没有要好----所以推荐你在优化程序时加上“-g”选项,即为了开发,也为了部署。GNU发行的软件包默认都打开了调试选项“-g”和优化选项“-O2”。
============================
GCC的C++前端用到很多同C编译器gcc相同的选项。它也支持另外一些选项来控制C++才有的语言特性。在使用g++时,一个很自然的不同点是“-ansi”选项的要求是兼容于C++标准,而不是C标准。
要注意的是,C++对象文件必须用g++来链接,以便与适当的C++库链接。试图用C编译器gcc来链接C++对象文件会导致由于找不到C++标准库函数而报“未定义引用”的错。
由GCC提供的C++标准库“libstdc++”除了包含象排序等泛型算法外,还包含很多象列表和队列等的泛型容器类。这些类原本是标准模板库(STL)的一部分,是一个独立的包,但现在已经被包括进C++标准库中。
由g++创建的使用C++标准库的可执行文件会被链接到共享库“libstdc++”,该库作为GCC的一部分而被默认安装。它有几个版本----如果你要发布使用C++标准库的可执行文件,你需要确保接受方安装了“libstdc++”的兼容版本,或者你索性用“-static”命令行选项来静态链接你的程序。
g++中使用模板的推荐方法是遵循“包含编译模型(inclusion compilation model)”,即把模板的定义放到头文件中(所以,现在有很多的库文件称之为header-only library,见c++中的header-only library)。GCC的C++标准库本身就使用该方法。用到模板的源文件通过“#include”指示符包括入模板所在的头文件。
查看完整选项:gcc –v –help
GNU归档工具ar用于把多个对象文件组合成归档文件,也被称作库。归档文件是一种把相关各个对象文件打包在一起发行的简便方法。
ar cr libhello.a hello_fn.o bye_fn.o
选项“cr”代表“create and replace”。
ar t libhello.a
hello_fn.o
bye_fn.o
选项“t”来列出已有库中的对象文件。
注意,当发行一个库时,该库提供的公开的函数和变量相关的头文件应该是可获得的,以便最终用户能include该头文件以获得正确的函数原型。
“-S”指导gcc把预处理过的C源代码转变成汇编语言,但不生成对象文件(obj文件)
在源代码文件被编译成对象文件或可执行文件以后,编译时指定的编译选项就不再那么容易知道了。file命令查看对象文件或可执行文件的内容来查看它的特性,比如象它是动态链接的还是静态链接的。
[root@iZ23i5mx5vxZ bin]# file mysql
mysql: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
not stripped:可执行文件包含符号表(符号表可以用strip命令删除)。
nm命令可以看到符号表,如:
[root@iZ23i5mx5vxZ bin]# nm mysql | more U ASN1_STRING_data@@libcrypto.so.10 U ASN1_STRING_length@@libcrypto.so.10 U BIO_free@@libcrypto.so.10 U BIO_new_bio_pair@@libcrypto.so.10 U BIO_new_mem_buf@@libcrypto.so.10 U BN_bin2bn@@libcrypto.so.10 U CRYPTO_cleanup_all_ex_data@@libcrypto.so.10 U CRYPTO_free@@libcrypto.so.10 U CRYPTO_malloc@@libcrypto.so.10 U CRYPTO_num_locks@@libcrypto.so.10 U CRYPTO_set_dynlock_create_callback@@libcrypto.so.10 U CRYPTO_set_dynlock_destroy_callback@@libcrypto.so.10 U CRYPTO_set_dynlock_lock_callback@@libcrypto.so.10 U CRYPTO_set_id_callback@@libcrypto.so.10 U CRYPTO_set_locking_callback@@libcrypto.so.10 000000000048fcc0 r CSWTCH.141 000000000048fc80 r CSWTCH.169 0000000000937040 d CZ_SORT_TABLE U DH_free@@libcrypto.so.10 U DH_new@@libcrypto.so.10 0000000000941898 V DW.ref.__gxx_personality_v0 U EC_KEY_free@@OPENSSL_1.0.1_EC U EC_KEY_new_by_curve_name@@OPENSSL_1.0.1_EC U ERR_clear_error@@libcrypto.so.10 U ERR_error_string_n@@libcrypto.so.10 U ERR_free_strings@@libcrypto.so.10 U ERR_get_error@@libcrypto.so.10 U ERR_get_error_line_data@@libcrypto.so.10 U ERR_remove_state@@libcrypto.so.10 U ERR_remove_thread_state@@libcrypto.so.10 U EVP_CIPHER_CTX_cleanup@@libcrypto.so.10 U EVP_CIPHER_CTX_init@@libcrypto.so.10 U EVP_CIPHER_CTX_set_padding@@libcrypto.so.10 U EVP_CIPHER_block_size@@libcrypto.so.10
nm命令的最常应用是检查某个库是否包含的特定函数的定义,通过查找第二列为“T”项的函数名即可。
ldd命令用于检查可执行文件并显示它需要的共享库的列表。这些库被称为该可执行文件的共享库依赖。如:
[root@iZ23i5mx5vxZ bin]# ldd mysql linux-vdso.so.1 => (0x00007fff61d61000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00000034cec00000) libreadline.so.6 => /lib64/libreadline.so.6 (0x000000340b600000) libtinfo.so.5 => /lib64/libtinfo.so.5 (0x000000340a600000) libz.so.1 => /lib64/libz.so.1 (0x00000034ce400000) librt.so.1 => /lib64/librt.so.1 (0x0000003772400000) libssl.so.10 => /usr/lib64/libssl.so.10 (0x00000034d0400000) libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00000034cfc00000) libdl.so.2 => /lib64/libdl.so.2 (0x00000034cf000000) libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000003af6800000) libm.so.6 => /lib64/libm.so.6 (0x00000034cf800000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003af6400000) libc.so.6 => /lib64/libc.so.6 (0x00000034ce800000) /lib64/ld-linux-x86-64.so.2 (0x00000034ce000000) libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x0000003420400000) libkrb5.so.3 => /lib64/libkrb5.so.3 (0x0000003421000000) libcom_err.so.2 => /lib64/libcom_err.so.2 (0x000000341fc00000) libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x0000003421400000) libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x0000003420c00000) libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x0000003420000000) libresolv.so.2 => /lib64/libresolv.so.2 (0x00000034d0800000) libselinux.so.1 => /lib64/libselinux.so.1 (0x000000341f800000)
ldd命令也能够用于检查共享库本身,可以跟踪共享库依赖链。
gcc默认全部导出符号表,vc默认全部不导出,所以在vc中调用dll时会涉及到导出导入符号表的管理,可参考https://msdn.microsoft.com/en-us/library/9h658af8(v=vs.110).aspx、https://msdn.microsoft.com/en-us/library/8fskxacy(v=vs.110).aspx。
gcc关于如何查看清除目标文件中的符号表,可参考http://blog.csdn.net/swedenfeng/article/details/53417085。