Linux 下文件编译与调试
gcc/g++编译器
对于.c格式的 C文件,可以采用gcc或g++编译
对于 .cc、.cpp格式的C++文件,应该采用g++进行编译
|
常用的选项:
-c 表示编译源文件
-o 表示输出目标文件
-g 表示在目标文件中产生调试信息,用于gdb调试
-D <宏定义> 编译时将宏定义传入进去
-Wall 打开所有类型的警告。
| |
1. gcc编译过程:预编译->编译->汇编->链接 |
(1)预处理:预处理器将对源文件中的宏进行展开。
(2)编译:gcc将c文件编译成 汇编文件。
(3)汇编:汇编器as将汇编文件编译成机器码。
(4)链接:将目标文件和外部符号进行连接,得到一个可执行二进制文件。
//test.c
| |
(1)预处理: gcc –E test.c -o test.i
我们用 cat 查看 test.i 的内容如下:
int main() int x=(1+2); return 0;我们可以看到,文件中宏定义
NUMBER出现的位置被(1+2)替换掉了,其它的内容保持不变。
//前面引入了很多东西
预处理阶段:对包含的头文件(#include)和宏定义(#define、#ifdef等)进行处理
|
(2)编译:gcc -S test.i –o test.s 通过 cat test.s 查看 test.s 的内容为
代码。
编译阶段:检查代码规范性、语法错误等,在检查无误后把代码翻译成汇编语言
| |
(3)汇编:as test.s -o test.o 利用as将汇编文件编译成机器码。得到输
出文件为 test.o。test.o 中为目标机器上的二进制文件. 用 nm 查
看文件中的符号: nm test.o 输出如下:0000000000000000 T main。
既然已经是
二进制目标文件了,能不能执行呢?试一下 ./test.o,提示
cannotexecute binary file.
T表示这个符号属于代码。
//test.o 得先加一个执行权限
| (4)链接: gcc test.o –o test ,将所有的.o文件链接起来生产可执行程序。 链接阶段:将.s的文件以及库文件整合起来链接为可执行程序 |
gcc所支持后缀名: | gcc常用选项: |
gcc库选项: | gcc警告选项: |
file 文件名 看对应文件类型 gcc -v 显示编译器版本 |
//test1.c
#include<stdio.h>
int main()
{
#ifdef meihao
printf("define meihao\n");
#else
printf("not define meihao\n");
#endif
printf("main exit\n");
}
| gcc -E test1.c -o test1.i //没有定义meihao gcc -E test1.c -o test1.i -D meihao |
函数库分为静态库和动态库
静态库是目标文件.a 的归档文件(格式为libname.a)。如果在编译某
个程序时链接静态库,则链接器将会搜索静态库并直接拷贝该程序
的可执行二进制文件到当前文件中;
静态库将会整合到程序中,在程序执行时不用加载静态库。 因此,静态库会使你的程序臃肿并且难以升级,但比较容易部署。而动态库会使你的程序轻便易于升级但难以部署。
|
动态库(格式为 libname.so[.主版本号.次版本号.发行号])。在程序编
译时并不会被链接到目标代码中,而是在程序运行时才被载入。
动态库只在执行时才被链接使用,不是直接编译为可执行文件,并且一个动态库可以被多个程序使用故可称为共享库。
| |
创建静态库: >gcc -c add.c //编译add.c源文件生成add.o目标文件 >ar -crsv libadd.a add.o //对目标文件*.o进行归档,生成lib*.a,此处lib要写;命令参数: c 创建一个库。不管库是否存在,都将创建。 r 代替库中现有的文件或者插入新的文件 s 写入一个目标文件索引到库中,或者更新一个存在的目标文件索引 v 输出详细信息 a - add.o >su #cp ./libadd.a /lib //拷贝到标准程序设计库 >gcc main.c -w -o main -ladd
>./main 1 2
sum=3
>
gcc -c add.c
>ar -crsv libadd.a add.o
a - add.o
>gcc main.c -w -o main -L./ -ladd
//不要忘记-L后面的那个./ ;即在库文件的搜索路径中添加当前路径 -ladd表示链接库文件libadd.a/.so
>./main 1 2
sum=3
|
创建动态库:
>gcc -fPIC -Wall -c add.c //编译,允许发出警告,生成使用相对地址的位置无关的目标代码
>gcc -shared -o libadd.so add.o //生成库文件libadd.so
>su
#cp ./libadd.so /lib
#exit //拷贝到 /lib 库中
>gcc main.c -w -o main -ladd
>./main 1 2
sum=3
在运行main前,需要注册动态库的路径。
方法有3种:
▶修改/etc/ld.so.conf
▶修改LD_LIBRARY_PATH环境变量
| |
//add.c
int add(int a,int b)
{
return a+b;
}
| //main.c
#include<stdio.h>
#include<stdlib.h>
void main(int argc,char* argv[])
{
if(argc!=3)
{
printf("error args\n");
return;
}
int i=atoi(argv[1]);
int j=atoi(argv[2]);
printf("sum=%d\n",add(i,j));
}
|
程序调试
gdb常用命令
Linux 包含了一个叫gdb的调试程序。gdb可以用来调试 C 和 C++ 程序。
在程序编译时用 -g 选项可打开调试选项,表示在目标文件中产生调试信息,用于gdb调试;
>gcc test1.c -Wall -g -o test //(—g和—o要挨在一起)(—g和—c要挨在一起)
>gdb ./test
|
core dumped
>ulimit -c unlimited
//test3.c
>gcc test3.c -w -o test3
>gdb ./test3 core
//l 输出错误行代码,bt 打开调用栈查看自己错误行代码, q 退出;
#include<stdio.h>
int add(int a,int b)
{
return a+b;
}
int main(int argc,char **argv)
{
int i,j,sum;
i=atoi(argv[1]);
j=atoi(argv[2]);
sum=add(i,j);
printf("sum=%d\n",sum);
}
gcc对代码进行优化通过选项“-On”来控制优化级别(n是整数)。不同的优化级别对应不同的优化处理工作。如使用优化选项“-O1”主要进行线程跳转和延迟退栈两种优化。使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,还要进行一些额外的调整工作,如处理其指令调度等。选项“-O3”则还包括循环展开或其他一些与处理器特性相关的优化工作。虽然优化选项可以加速代码的运行速度,但对于调试而言将是一个很大的挑战。因为代码在经过优化之后,原先在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也有可能因为循环展开而变得到处都有,所有这些对调试来讲都是不好的。所以在调试的时候最好不要使用任何的优化选项,只有当程序在最终发行的时候才考虑对其进行优化。
通常用的是-O2