Linux下的程序设计主要指C程序设计,它与其他环境中的C程序设计一样,主要涉及到编辑器、编译链接器、调试器及项目管理工具。
GNU CC编程
GCC编译器能将C、C++语言源程序、汇编程序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,GCC将生成一个名为a.out的文件。
在Linux系统中,编译器通过程序的扩展名可分辨出编写原始程序代码所用的语言,由于不同程序所需要执行编译的步骤是不同的。因此GCC可根据不同的扩展名对它们进行分别处理。
GCC所支持的扩展名文件
GCC 编译过程
编译过程分为四个阶段
预处理(Pre-Processing)
编译(Compiling)
汇编(Assembling)
链接(Linking)
基本语法格式
gcc [选项] 源文件 [选项] [目标文件]
举例
$gcc -E hello.c -o hello.i
#“-E” 指定只进行预处理
#“hello.c”是源程序文件
#“-o”指定生成目标文件
#“hello.i”是预处理过程生成的目标文件
$gcc -S hello.i -o hello.S
#“-S”指定只进行到编译阶段
#“hello.i”是进行编译的源文件
#“-o”指定生成目标文件
#“hello.S”是编译生成的目标文件名
$gcc -c hello.S -o hello.o
#“-c”指定只进行到汇编阶段结束为止
#“hello.S”是进行汇编的源文件
#“-o”指定生成目标文件
#“hello.o”是编译生成的目标文件名。hello.o为二进制目标代码文件
$gcc hello.o -o hello
#该命令gcc之后无选项参数,表示对指定的源文件进行编译,直到输出执行文件(示例中的源文件为hello.o,输出的执行文件hello)。
$./hello
#运行该可执行文件
函数库创建和使用
函数库一般分为静态库和动态库两种。
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”。
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时运行链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的libc.so.6就是动态库。GCC在编译时默认使用动态库。
- 静态库创建步骤一
编辑源文件libhello.h
#ifndef _libhello_H_
#define _libhello_H_
void print_hello(void);
#endif
编辑源文件libhello.c
#include <stdio.h>
void print_hello(void)
{
printf(“hello library\n”);
}
- 静态库创建步骤二
生成目标文件libhello.o
$gcc –c libhello.c –o libhello.o
- 静态库创建步骤三
使用ar命令创建静态库libhello.a
$ar –rc libhello.a libhello.o
$file libhello.a
- 静态库的使用
编辑测试文件usehello.c
生成可执行文件usehello_static
$gcc –o usehello_static usehello.c libhello.a
- 运行
$./usehello_static
动态库的创建
- 编辑源文件libhello.h,libhello.c
- 生成共享库目标文件libhello.o
$gcc –fPIC –Wall –g
- 生成共享库目标文件libhello.o
$gcc –fPIC –Wall –g–c libhello.c –o libhello.o
- 编译共享库libhello.so.1.0
$gcc –g –shared –W1,-soname,libhello.so –o libhello.so.1.0 libhello.o
$file libhello.so.1.0
- 创建共享库的符号连接
$ln –s libhello.so.1.0 libhello.so
- 动态库的使用
编辑测试文件usehello.c
生成可执行文件usehello_dy
$gcc –g –o usehello_dy usehello.c –lhello –L ./
- 运行
$LD_LIBRARY_PATH=$(pwd) ./usehello_dy
静态库与共享库的区别
可执行文件大小不同
- 当多个进程使用同一共享库,将共享库中存放的可执行代码在内存共享;
- 静态库将直接加载到可执行文件。
共享库可很大程度上节约系统内存。
共享库发生错误,只需要修改当前共享库,无需重新编译每个使用该库的程序
多个源文件编译
$gcc foo1.c foo2.c -o foo
解释:对于源文件不止一个情况,GCC编译过程仍然按照预处理、编译、汇编和链接的过程依次进行。因此,上面这条命令相当于依次执行如下三条命令。
$gcc -c foo1.c -o foo1.o
$gcc -c foo2.c -o foo2.o
$gcc foo1.o foo2.o -o foo
GCC选项汇总
GCC选项——-I dir选项
在当前路径下有文件hello1.c ,“/root/work/gcc/”目录下有my.h:
编译命令:
$gcc hello1.c -I /root/work/gcc/ -o hello1
GCC选项——==-L dir ==选项
“-L dir”的功能与“-I dir”类似,能够在库文件的搜索路径列表中添加dir目录。例如有程序hello2.c需要用到目录“/root/work/gcc/lib/” /下的一个动态库libxch.so,则只需键入如下命令即可:
$gcc hello2.c –L root/work/gcc/lib/ hello2
注意:“-I dir”和“-L dir”都只是指定了路径,而没有指定文件,因此不能在路径中包含文件名。
调试—GDB
GDB概述
对于Linux程序员来讲,GDB(GNU Debugger)通过与GCC的配合使用,为基于Linux的软件开发提供了一个完善的调试环境。
将调试符号插入到生成的二进制代码中的gcc编译
$gcc -g hello.c -o hello
GDB调试举例
#【GDBTest.c】
#include <stdio.h>
void sum(int m);
int main()
{
int i,n=0;
sum(50);
for(i=1; i<=50; i++)
{
n += i;
}
printf("The sum of 1-50 is %d \n", n );
return(0);
}
void sum(int m)
{
int i,n=0;
for(i=1; i<=m;i++) n += i;
printf("The sum of 1-m is %d\n", n);
}
- 首先使用GCC对test.c进行编译。
注意:GDB进行调试的是可执行文件,而不是如“.c”的源代码,因此,需要先通过GCC编译生成可执行文件才能用GDB进行调试。编译时一定要加上选项“-g”,这样编译出的可执行代码中才包含调试信息,否则Gdb无法载入该可执行文件。
$gcc -g test.c -o test
- 进入GDB调试环境
$gdb test
-
查看程序源文件
(gdb) list
-
设置断点
(gdb) break 6
-
查看断点设置情况
(gdb) info b
-
运行程序
(gdb) run
-
查看变量值
(gdb) print n
(gdb) print i
-
单步运行
(gdb)next
(gdb)step -
恢复程序运行
(gdb) c
GDB调试选项
- GDB的命令可以通过查看help进行查找
(gdb) help
(gdb) help data
(gdb) help call - GDB中的命令主要分为以下几类:
1.工作环境相关命令;
2.设置断点与恢复命令;
3.源代码查看命令;
4.查看运行数据相关命令及修改运行参数命令。 - 工作环境相关命令
- 断点设置与恢复命令
1.函数断点
(gdb) break sum
2.条件断点
(gdb) break 8 if i= =10 - 源码查看相关命令
- 查看运行数据相关命令
- 修改运行参数相关命令
1.修改运行时的参数,并使该变量按照用户当前输入的值继续运行。设置方法:在单步执行的过程中,键入命令“set 变量=设定值”
GNU make
make简介
Make工程管理器
- 一个“自动编译管理器”
- GNU的make能够使整个软件工程的编译、链接只需要一个命令就可以完成。
- “自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量。
- Linux程序员必须学会使用GNU make 来构建和管理自己的软件工程。
常用的make命令行选项
Makefile
- Make在执行时需要一个命名为Makefile的文件
- Makefile是一个文本形式的数据库文件
- 描述了整个工程的编译、链接等规则
- 工程中的哪些文件需要编译,具体怎么编译
- 需要创建哪些库文件以及如何创建这些库文件
- 如何产生我们最后想要得到的可执行文件
- make在执行时就会找到Makefile文件
Makefile实例
makefile的编写
Makefile术语
- 规则
用于说明如何产生一个或者多个目标文件
规则格式:
target: dependency
COMMAND
实例
注意:在command前面有tab,这个不可缺少
- 伪目标
Makefile中把那些没有任何依赖只有执行动作的目标称为“伪目标”
实例
“.PHONY”将“clean”目标声明为伪目标 - 变量
- Makefile其他变量
除前面介绍的用户自定义变量(如OBJS、HDRS)外,make也允许使用环境变量、自动变量和预定义变量。
-
环境变量——工作环境的相关变量,如路径、时间等。
-
自动变量——用于代表编译语句中出现的目标文件和依赖文件,并且具有本地含义。
-
预定义变量——通常指编译器、汇编器的名称及其编译选项。
-
系统默认自动变量
自动变量使用示例
-
预定义变量
-
隐式规则
Make会自动搜索隐式规则目录来确定如何生成文件。
举例
常见隐式规则
-
模式规则
模式规则是用来定义相同处理规则的多个文件的。它不同于隐式规则,隐式规则仅仅能够用make默认的变量来进行操作,而模式规则还能引入用户自定义变量,为多个文件建立相同的规则,从而简化Makefile的编写。
模式规则的格式类似于普通规则,这个规则中的相关文件前必须用“%”标明。
-
Makefile注释
makefile文件中只有行注释,其注释用“#”字符,就像C语言中的“//”,如果要在makefile文件中使用“#”字符,可以使用“\”进行转义,即“#”。 -
Makefile-函数
$(subst from,to,text)
功能:在文本text中使用to替换每一处form。
举例:$(subst ee,EE,feet on the street)
$(patsubst pattern,replacement,text)
功能:寻找text中符合pat的字,并repl替换它们。
举例:$(patsubst %.c,%.o,x.c.c bar.c)
$(filter pattern……,text)
功能:返回在text中有空格隔开并匹配pat的字。
举例:sources := foo.c bar.c bar.s ug.h
foo:$(sources)
cc $(filter %.c %.s,$(sources)) -o foo
$(filter-out pattern……,text)
$(wildcard pattern)
功能:寻找与格式匹配且文件存在的文件名
举例:$(wildcard %.c ../src/%.c)
$(addprefix prefix,names……)
功能:将prefix附加在每一个独立文件名的前面。
举例:$(addprefix src/,foo bar)