在阅读的过程中有任何问题,欢迎一起交流
QQ:1494713801
在linux下,开发工具被切割成一个个独立的小工具。各自处理不同的问题。例如:
编辑器(emacs, vim)用来进行编辑程序的
调试器(gdb) 用来调试程序
编译器(GCC) 用来编译和链接程序的
性能分析工具(gcov, gprof) 用来优化程序的
文档生成器(doxygen)
用来生成文档的
同时,还有一些系统工具和系统知识,我们是很有必要了解的:
makefile 程序自动化机制
shell 系统粘合剂
grep, locate, find
系统查找工具
其它的工具(例如ctags, OCI公司的MPC等等)
以一个C++源文件为例:
//test.c:
#include <stdio.h>
int main(int args,char **argv)
{
printf("hello world!");
return 0;
}
======================================
一、
1. 编译过程:.c -- 预处理(processing)->.i -- 编译(compilation)->.s -- 汇编(assembly) - >
.o -- Linking --
2. 预处理:
gcc -E test.c -o test.i / gcc -E test.c
预处理的结果就是将stdio.h文件中的内容插入到test.c中, -o表示输出汇编代码文件。
3.编译为汇编代码:
gcc -S test.i -o test.s
-S选项表示生成汇编代码
4. 汇编:
gcc -C test.s -o test.o
汇编器将汇编代码编译成目标文件
5. 链接:
gcc test.o -o test
二、
1.对多个文件进行编译:
gcc test1.c test2.c -o test
2.检错
gcc -pedantic illcode.c -o illcode
-pedantic 帮助程序员发现不符合ansi/iso标准代码。
-Wall 使gcc产生尽可能多的警告信号
-Werror会在警告的地方停止编译,迫使程序员对自己代码进行修改
三、库文件的链接:
函数库是由一些头文件(.h)和库文件(.so, .lib, .dll)的集合。 LINUX默认将头文件放在/usr/include/, 库文件放在/usr/lib/; 如果我们要用的库不在这些目录下,所以在gcc编译的时候必须用自己的办法来查找所需要的头文件和库文件。
例: test.c链接mysql,我们要下载mysql的库——MySQL Connectors, 下下来以后由个include的文件夹, 里面包含头文件, 还有一个lib的文件夹,里面包含二进制so文件libmysqlclient.so,其中include的路径是/usr/dev/mysql/include, lib的文件夹是/usr/dev/mysql/lib
1. 编译成目标文件:
gcc -c -I /usr/dev/mysql/include test.c -o test.o
2. 链接:最后我们把所有的目标文件链接成可执行文件。
gcc -L /usr/dev/mysql/lib -lmysqlclient test.o -o test
linux下动态链接库用so结尾,静态链接库由a结尾。
3.强制链接时使用静态链接库:
加-static
gcc -L /usr/dev/mysql/lib -static -lmysqlclient test.o -o test
静态库搜索顺序
(1). ld 会去找gcc命令中参数-L
(2)再找gcc环境变量library_PATH
(3)再找内定目录/lib, /usr/lib, /usr/local/lib
动态链接搜索顺序:
(1)编译目标代码时候指定的搜索路径
(2)环境变量LD_LIBRARY_PATH
(3)配置文件/etc/ld.so.con
(4)/lib, /usr/lib
GDB调试:
1. gcc -g main.c -o main
在用gcc时候,加上-g表示在生成的目标文件中加入源代码信息以便调试。如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。当你用
-g把调试信息加入之后,并成功编译目标代码以后,即可以使用gdb来调试他。
2. l, list 从第一行开始列出源代码
3. start, 开始执行程序,第一行break
4. n, next下一行
5. s, step进入函数
6. bt, backtrace查看函数栈
7. i locals, 用info命令查看局部变量
8. f 1, 到栈帧1
9. p sum, print出sum的值
10. finish, 运行到返回点——如果是从s进来的函数
11. set var sum=0, 调试过程中给变量sum赋值
12. p result[2]=33, 用p赋值
13. display sum, 每次程序停止的时候显示sum的值
14. b, break当前的循环
15. b 9, 在第9行设置断点
16. c, 连续运行continue
17. i breakpoints, 查看所有断点
18. delete breakpoint 2, 删除断点2
19. disable breakpoint 2 , 禁用断点2
20. enable breakpoint 2 , 启用
21. break 9 if sum!=0 , 当满足条件时候断点激活
22. r , 从头开始执行run
示例:
编译生成执行文件:(Linux下)
hchen/test> cc -g tst.c -o tst
使用GDB调试:
hchen/test> gdb tst <---------- 启动GDB
GNU gdb 5.1.1
Copyright 2002 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. Type "show warranty" for details.
This GDB was configured as "i386-suse-linux"...
(gdb) l <-------------------- l命令相当于list,从第一行开始例出原码。
1 #include
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i 7 {
8 sum+=i;
9 }
10 return sum;
(gdb) <-------------------- 直接回车表示,重复上一次命令
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
(gdb) break 16 <-------------------- 设置断点,在源程序第16行处。
Breakpoint 1 at 0x8048496: file tst.c, line 16.
(gdb) break func <-------------------- 设置断点,在函数func()入口处。
Breakpoint 2 at 0x8048456: file tst.c, line 5.
(gdb) info break <-------------------- 查看断点信息。
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048496 in main at tst.c:16
2 breakpoint keep y 0x08048456 in func at tst.c:5
(gdb) r <--------------------- 运行程序,run命令简写
Starting program: /home/hchen/test/tst
Breakpoint 1, main () at tst.c:17 <---------- 在断点处停住。
17 long result = 0;
(gdb) n <--------------------- 单条语句执行,next命令简写。
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) n
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) c <--------------------- 继续运行程序,continue命令简写。
Continuing.
result[1-100] = 5050 <----------程序输出。
Breakpoint 2, func (n=250) at tst.c:5
5 int sum=0,i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p i <--------------------- 打印变量i的值,print命令简写。
$1 = 134513808
(gdb) n
8 sum+=i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$2 = 1
(gdb) n
8 sum+=i;
(gdb) p i
$3 = 2
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$4 = 3
(gdb) bt <--------------------- 查看函数堆栈。
#0 func (n=250) at tst.c:5
#1 0x080484e4 in main () at tst.c:24
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
(gdb) finish <--------------------- 退出函数。
Run till exit from #0 func (n=250) at tst.c:5
0x080484e4 in main () at tst.c:24
24 printf("result[1-250] = %d /n", func(250) );
Value returned is $6 = 31375
(gdb) c <--------------------- 继续运行。
Continuing.
result[1-250] = 31375 <----------程序输出。
Program exited with code 027. <--------程序退出,调试结束。
(gdb) q <--------------------- 退出gdb。
hchen/test>
Makefile基础:
1. Makefile 是由一组rule组成, 每条rule信息如下:
target ...: prerequistites ...
<tab>command1
<tab>command2
例:
main: main.o stack.o maze.o
gcc main.o stack.o maze.o -o main
只要有一个prerequisities更新了,目标也会被更新,就是执行command
2. clean规则
用于清除编译产生的二进制文件,保留源文件:
clean:
@echo "cleaning project"
-rm main *.o
@echo "clean completed"
如果命令前加@则不显示命令本身,只显示结果。 如果加-表示即使命令出错也不会停止。 通常rm或者mkdir前面要加-,因为可能没有这个文件,或者已经有了这个文件。
如果存在有文件名字就叫做clean,则会出错,那么就要添加一行,将clean关键字申明成伪目标
.PHONY:clean
3. 4个规则关键字:
install:将可执行文件、配置文件、docs分别拷贝到不同安装目录
all:执行主要编译工作,通常用作缺省目标
clean:删除编译生成的二进制文件
distclean:不仅删除二进制文件,还删除其他的,只留下源文件。
4. 隐含规则和模式规则:
其中有一条
%.o: %.c
$(compile.c) $(output_option) $<
[email protected]为规则中的target, $<为规则中的第一个条件,上面那句相当于 cc -c [email protected] $<
进而相当于所有符合这样的依赖关系:
main.o:main.h statk.h maze.h
可以隐含
main.o: main.c
cc -c o main.o main.c
5. 变量:
main.o: main.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
CC = gcc
CFLAGS = -o -g
CPPFLAGS = -Iinclude
:=, 立即赋值
?=, 如果没有赋过值才进行赋值
+=, 追加赋值
$^, 表示将所有条件组成的列表
$?, 表示所有比目标新的条件组成的列表
6. 自动处理依赖关系:
例:
all: main
main: main.o stack.o maze.o
gcc $^ -o [email protected]
clean:
-rm main *.o
.PHONY: clean
sources = main.c stack.c maze.c
include $(souces: .c = .d)
%.d: %.c
set -e; rm -f [email protected];\
$(CC) -MM $(CPPFLAGS) $< > [email protected]$$$$;\ # $$$$相当于两个$ , 表示进程
....略...