编译:
第一步、是进行预编译,使用-E参数可以让GCC在预处理结束后停止编译过程:
gcc -E hello.c -o hello.i
预处理的宏定义插入到hello.i中
第二步、是将hello.i编译为目标代码,这可以通过使用-c参数来完成:
gcc -c hello.i -o hello.o
也可以通过源文件直接生成
gcc -c hello.c
第三步、是将生成的目标文件链接成可执行文件:
gcc hello.o -o hello
也可以通过源文件直接生成
gcc -o hello hello.c
警告:
1、-pedantic 选项,那么使用了扩展语法的地方将产生相应的警告信息
2、-Wall 使用它能够使GCC产生尽可能多的警告信息
3、-Werror,它要求GCC将所有的警告当成错误进行处理
库依赖:
1、Linux下的大多数函数都默认:
头文件放到/usr/include/目录下
而库文件则放到/usr/lib/目录下
2、GCC在编译时必须有自己的办法来查找所需要的头文件和库文件。
-I选项可以向GCC的头文件搜索路径中添加新的目录。
例如,如果在/home/yy/include/目录下有编译时所需要的头文件,为了让GCC能够顺利地找到它们,就可以使用-I选项:
gcc -o test test.c -I /home/yy/include
-L选项向GCC的库文件搜索路径中添加新的目录
例如,如果在/home/yy/lib/目录下有链接时所需要的库文件libfoo.so,为了让GCC能够顺利地找到它,可以使用下面的命令
gcc -o test test.c -L /home/yy/lib -lfoo
值得好好解释一下的是-l选项,它指示GCC去连接库文件libfoo.so。
Linux下的库文件在命名时有一个约定,那就是应该以lib三个字母开头,由于所有的库文件都遵循了同样的规范,因此在用-l选项指定链接的库文件名时可以省去lib三个字母,也就是说GCC在对-lfoo进行处理时,会自动去链接名为libfoo.so的文件。
-static选项,强制使用静态链接库
如果在/home/yy/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a
为了让GCC在链接时只用到静态链接库,可以使用下面的命令:
gcc -o test test.c -L /home/yy/lib -static -lfoo
编译成动态库:
g++ -fPIC -shared -o libfunc.so func.cpp
-fPIC参数
表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码,概念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。
选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1。
在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。
选项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。
选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作。
通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。
许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点。
time ./test 查看程序执行时间
优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码:
程序开发的时候优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。
资源受限的时候一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。
跟踪调试的时候在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。
加速:
使用管道代替编译中临时文件,
-pipe 加速编译
gcc -pipe foo.c -o foo
GCC常用选项
-c 通知GCC取消链接步骤,即编译源码并在最后生成目标文件;
-Dmacro 定义指定的宏,使它能够通过源码中的#ifdef进行检验;
-E 不经过编译预处理程序的输出而输送至标准输出;
-g3 获得有关调试程序的详细信息,它不能与-o选项联合使用;
-Idirectory 在包含文件搜索路径的起点处添加指定目录;
-llibrary 提示链接程序在创建最终可执行文件时包含指定的库;
-O、-O2、-O3 将优化状态打开,该选项不能与-g选项联合使用;
-S 要求编译程序生成来自源代码的汇编程序输出;
-v 启动所有警报;
-Wall 在发生警报时取消编译操作,即将警报看作是错误;
-Werror 在发生警报时取消编译操作,即把报警当作是错误;
-w 禁止所有的报警。
静态链接
在这种连接方式下,函数的代码将直接拷贝到最终的可执行文件中。该程序被执行时候,会被装入该进程的虚拟地址空间中。静态链接库实际上是一个或若干目标文件。
动态链接
这种方式下,函数的代码被放到称作动态连接库或共享对象的某个目标文件中。链接程序此时的工作只是在生成的可执行文件中,记录下共享对象的名字以及少量关键信息。动态连接库可以被多个进程共享,在运行时候内存中只有一个实例。
二者比较
使用动态链接能够使可执行文件较小,并且当共享对象被多个进程使用时节省内存。但有时候系统运行改变或升级,不能保证动态连接库一定可用、有效。
gcc调试段错误
我们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难免会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。但是手工“除虫”(debug),往往是效率低下且让人厌烦的,本文将就"段错误"这个内存访问越界的错误谈谈如何快速定位这些"段错误"的语句。
下面将就以下的一个存在段错误的程序介绍几种调试方法:
|
xiaosuo@gentux test $ ./a.out 段错误 |
1.利用gdb逐步查找段错误:
这种方法也是被大众所熟知并广泛采用的方法,首先我们需要一个带有调试信息的可执行程序,所以我们加上“-g -rdynamic"的参数进行编译,然后用gdb调试运行这个新编译的程序,具体步骤如下:
xiaosuo@gentux test $ gcc -g -rdynamic d.c xiaosuo@gentux test $ gdb ./a.out GNU gdb 6.5 Copyright (C) 2006 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. This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1". (gdb) r Starting program: /home/xiaosuo/test/a.out Program received signal SIGSEGV, Segmentation fault. 0x08048524 in dummy_function () at d.c:4 4 (gdb) |
从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man 7 signal),我们知道SIGSEGV默认handler的动作是打印”段错误"的出错信息,并产生Core文件,由此我们又产生了方法二。
2.分析Core文件:
Core文件是什么呢?
The |
xiaosuo@gentux test $ ulimit -c 0 xiaosuo@gentux test $ ulimit -c 1000 xiaosuo@gentux test $ ulimit -c 1000 xiaosuo@gentux test $ ./a.out 段错误 (core dumped) xiaosuo@gentux test $ ls a.out |
xiaosuo@gentux test $ gdb ./a.out core GNU gdb 6.5 Copyright (C) 2006 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. This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1". warning: Can't read pathname for load map: 输入/输出错误. 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 Core was generated by `./a.out'. Program terminated with signal 11, Segmentation fault. #0 4 |
接着考虑下去,以前用windows系统下的ie的时侯,有时打开某些网页,会出现“运行时错误”,这个时侯如果恰好你的机器上又装有windows的编译器的话,他会弹出来一个对话框,问你是否进行调试,如果你选择是,编译器将被打开,并进入调试状态,开始调试。
Linux下如何做到这些呢?我的大脑飞速地旋转着,有了,让它在SIGSEGV的handler中调用gdb,于是第三个方法又诞生了:
3.段错误时启动调试:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> void dump(int signo) { } dummy_function (void) { } main (void) { } |
xiaosuo@gentux test $ gcc -g -rdynamic f.c xiaosuo@gentux test $ ./a.out GNU gdb 6.5 Copyright (C) 2006 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. This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1". Attaching to program: /home/xiaosuo/test/a.out, process 9563 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 0xffffe410 in __kernel_vsyscall () (gdb) bt #0 #1 #2 #3 #4 #5 #6 |
以上方法都是在系统上有gdb的前提下进行的,如果没有呢?其实glibc为我们提供了此类能够dump栈内容的函数簇,详见/usr/include/execinfo.h(这些函数都没有提供man page,难怪我们找不到),另外你也可以通过 gnu的手册 进行学习。
4.利用backtrace和objdump进行分析:
重写的代码如下:
#include <execinfo.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> dummy_function (void) { } void dump(int signo) { } main (void) { } |
xiaosuo@gentux test $ gcc -g -rdynamic g.c xiaosuo@gentux test $ ./a.out Obtained 5 stack frames. ./a.out(dump+0x19) [0x80486c2] [0xffffe420] ./a.out(main+0x35) [0x804876f] /lib/libc.so.6(__libc_start_main+0xe6) [0xb7e02866] ./a.out [0x8048601] |
xiaosuo@gentux test $ objdump -d a.out |
|
后记:
本文给出了分析"段错误"的几种方法,不要认为这是与孔乙己先生的"回"字四种写法一样的哦,因为每种方法都有其自身的适用范围和适用环境,请酌情使用,或遵医嘱。
Note:打印数字是却用%s也会出现段错误。