linux 下的 c 程序编程

37
1 Linux 下下 C 下下下下

Upload: xander

Post on 05-Jan-2016

415 views

Category:

Documents


12 download

DESCRIPTION

Linux 下的 C 程序编程. 主要内容. GCC 编译器. GDB 调试器. 一、 GCC 编译器 1 、 GCC 是什么. GCC ( the GNU Complier Collection,GNU 编译工具合集)。包括预编译( cpp )、编译( gcc ),与 make 、连接( ld )、调试( gdb )等多个编译过程的常用工具一起组成 GNU 开发工具链。 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Linux 下的 C 程序编程

1

Linux 下的C 程序编程

Page 2: Linux 下的 C 程序编程

2

主要内容

GCC 编译器

GDB 调试器

Page 3: Linux 下的 C 程序编程

3

一、 GCC 编译器

1 、 GCC 是什么GCC ( the GNU Complier Collection,GNU 编译工

具合集)。包括预编译( cpp )、编译( gcc ),与 make 、连接( ld )、调试( gdb )等多个编译过程的常用工具一起组成 GNU 开发工具链。

GCC1.0 于 1987 年 5 月发布,历经 20 余年的发展,已经成为一款功能强大的世界级优秀编译工具。支持多种语言的编译,包括 C 、 C++ 、 Ada 、 Fortran 、 Java等,并且包含这些语言相应的开发支持库(可到 GCC官网 http://gcc.gnu.org/下载所用开发语言的编译器, gcc 、 g++ 、 gcc-java 等 ) 。

Page 4: Linux 下的 C 程序编程

4

GCC 的优点 与其他一些软件公司出品的编译器比较, GCC 生成的

可执行文件具有代码长度短、执行效率高等特点。 具有较强的灵活性和高度的可移植性。安装 GCC 时做

些简单设置或对 GCC 源码进行少量修改,就可以使 GCC 完成在不同指令系统的交叉编译,极大地方便了嵌入式设备的软件开发。

可方便地与其他软件和协议对接。

Page 5: Linux 下的 C 程序编程

5

GCC 通过后缀来区别输入文件的类型,集成 C 、 C++ 、 JAVA 等多种语言编译器。是符合 ANSI C 标准的多平台编译器。∙ .c C 语言源代码文件。∙ .h 程序所包含的头文件。∙ .o 编译后的目标文件。∙ .C 、 .cpp 、 .cc 或 .cxx C++ 源代码文件。∙ ii 己经预处理过的 C++ 源代码文件。∙ .i 已经预处理过的 C 源代码文件。∙ .s 汇编语言源代码文件。∙ .S 经过预编译的汇编语言源代码文件。∙ a 由目标文件构成的档案库文件。

Page 6: Linux 下的 C 程序编程

6

网络支持下的命令安装sudo apt-get install gcc

sudo apt-get install build-essential

gcc –v ( 或 gcc --version)

类似的sudo apt-get install g++

sudo apt-get install gcj

源代码安装 GCC (选看)

find / -name gcc* find / -name g++* find / -name gcj*

Page 7: Linux 下的 C 程序编程

7

2 、 gcc 工作流程 gcc 是 c 语言程序编译器。一个 C 语言程序编译

过程如下:① 编辑:写源码② 预处理:系统加载头文件和展开宏,仅对文本信息处

理,是编译前的准备、整理。③ 编译:将 C 语言转换为对应的计算机机器码,生成二

进制文件。但若有多个模块,此时的文件还不能执行,因为各模块还相对独立,相互使用的地方还是标记。

④ 链接:将模块合成一个可执行的整体。 gcc 通过分别调用预处理、编译、链接工具,自

动完成 C 程序的编译工作。

Page 8: Linux 下的 C 程序编程

8

hello world

① 编辑 vi hello.c#include <stdio.h>

int main()

{

printf(“HELLO WORLD.THIS IS MY FIRST C PROGRAM”);

return 0;

}

② 编译执行gcc hello.c 编译生成可执行文件,结果为 a.out

或 gcc –o hey hello.c 编译生成可执行文件,指定结果文件名为 he

y

Page 9: Linux 下的 C 程序编程

9

3 、基本使用

GCC 有超过 100 个的编译选项。选项的合理使用对编译调试程序有很大作用。利用优化选项告诉 gcc 产生更小更快的可执行文

件,典型的如 -O 告诉 gcc 进行基本优化、 -O2产生尽可能小、快的代码。

利用调试选项方便调试剖析,如 -g 产生能被 gdb 使用的调试信息。

更多调试选项可查阅 man 及其他手册或资料。

Page 10: Linux 下的 C 程序编程

10

gcc 常用参数列表-o 指定输出的文件名

-g 产生符号调试工具 (GNU 的 GDB) 所必要的符号信息,要想对源代码进行调试,必须加入这个选项。

-c 进行预处理、编译、汇编,产生目标代码。输出的文件后缀默认为 .o

-O gcc 进行基本优化

-E 只进行预处理,结果输出到标准输出,除非用 -o 指定输出文件(一般指定文件后缀为 .i )

-S 进行预处理、编译,产生汇编代码。输出的文件后缀默认为 .s 。

-I dirname 指定额外的头文件搜索路径 dirname 目录

-L dirname 指定额外的函数库搜索路径 dirname 目录。

Page 11: Linux 下的 C 程序编程

11

举例1 ) Vi 编辑器编写代码vi chess.c #include <stdio.h>main(){ int i,j; for (i=0;i<8;i++) /*象棋棋盘图案 */ { for(j=0;j<8;j++) if((i+j)%2==0)

printf("%c%c“ ; 0xa1,0xf6); /* 输出黑色小方块( GB2312 编码) */ else printf(" "); printf("\n"); } printf("\n"); for(i=1;i<11;i++) /*阶梯图案 */ { for(j=1;j<=i;j++) printf("%c%c“,0xa1,0xf6); /* 输出黑色小方块( GB2312 编码) */ printf("\n"); }}

Page 12: Linux 下的 C 程序编程

12

2 )编译gcc chess.c –o chessGCC 给出报错信息, 9:expected ’)’ before ‘’ token

将“;”修改成“ ,” 后存盘退出。再编译一次。注意用上下方向键可使用历史命令,减少多次输入。

3 )执行在图形界面中设置“终端”→“设定字符编码”选中“简体中文( GB2312 )”。

./chess 执行看效果即可。

Page 13: Linux 下的 C 程序编程

13

多个文件的编译

vi main.c

#include <stdio.h>void chessboard();void stair();main(){ chessboard(); stair();}

vi chessboard.c

#include <stdio.h>void chessboard(){ int i,j; for (i=0;i<8;i++) { for(j=0;j<8;j++) if((i+j)%2==0) printf("%c%c",0xa1,0xf6); else printf(" "); printf("\n"); } printf("\n");}

vi stair.c

#include <stdio.h>void stair(){ int i,j; for(i=1;i<11;i++) { for(j=1;j<=i;j++) printf("%c%c",0xa1,0xf6); printf("\n"); } printf("\n");}

gcc main.c chessboard.c stair.c –o prngrap

Page 14: Linux 下的 C 程序编程

14

常见报错语法错误:

错误信息:文件 source.c中第 n 行有语法错误( syntex error )。头文件错误

错误信息:找不到头文件 head.h ( can not find include file head.h )。

函数库错误 错误信息:链接程序找不到所需的函数库。如: ld : -lm : No su

ch file or directory

Page 15: Linux 下的 C 程序编程

15

* 关于头文件与库文件 *

gcc 在编译时如何寻找所需要的头文件: ① header file 的搜寻会从 -I 开始 ② 然后找 gcc 的环境变量 C_INCLUDE_PATH 、 CPLUS_INCLUDE_PATH 、

OBJC_INCLUDE_PATH ③ 再找内定目录

/usr/include 用户空间头文件 /usr/local/include 本地头文件 /usr/src/include 内核态头文件。。。

gcc 怎么找库函数所在的库文件 ?① 先找 -L 指定的搜索目录

如 cos( ) 等函式库的选项要多加 – lm (指定 libm.so 库)② 再找 gcc 的环境变量 LIBRARY_PATH ③ 再找内定目录

/lib /usr/lib /usr/local/lib。。。

Page 16: Linux 下的 C 程序编程

16

可以用两个编译选项指定位置如: d1 目录下的 a.c 程序,用到 d2 目录下的 h1.

h 。gcc –o aexe a.c –I d2

(下面的 dir 代表一个目录名)-I dir :指定头文件所在目录。 gcc 先在指定目录 dir 下查找头文件,然后再按常规查找。

-L dir : gcc 搜索函数所在的库文件时先在 dir 中找,然后才到标准库中找。

Page 17: Linux 下的 C 程序编程

17

想实现某个功能,却不知道用哪个函数?查手册

《 Linux函数库参考手册》(适合不知道什么功能该用什么函数,以及函数的具体使用)

man (适合知道函数名后查具体使用)积累

多读、多做vi中在函数名处按 shift+k 可看到函数说明信息

为什么别人对用什么函数这么有数?上述就是答案。学会查!多积累经验!没有捷径!

Page 18: Linux 下的 C 程序编程

18

4 、代码管理工具 make GNU make :

编译处理较多文件时程序员就不需要每次编译都键入同样的大量的 gcc 命令。

利用 make 对包含一组文件的项目进行管理,采用增量编译,根据文件修改自动判断哪些需要重新编译,哪些利用上次结果,避免大量的重复计算,提高了编译效率。

makefile

make 工具通过一种脚本文件来完成自动编译,所有编译操作写在一个文件内,方便维护编译工作,默认该文件叫makefile 。 该文件定义多个源文件之间的依赖关系; 说明如何编译各个源文件并链接生成可执行文件。

Page 19: Linux 下的 C 程序编程

19

makefile 文件一般由一种两行的脚本单元(规则)组成,简单例子:

vi makefile

test:test.c test.h (目标文件:依赖文件)gcc test.c –o test (达成目标使用的命令,注意前要有一个 tab )

make

或 make test (目标名与 makefile中的要一致)

Page 20: Linux 下的 C 程序编程

20

1 、 makefile 文件语法:target : dependency_files (依赖关系)

(tab)command target (具体操作)如:

guess: main.o b.ogcc main.o b.o –o guess (此句前是 tab 不是空格)

main.o:main.c h1.hgcc –c main.c –o main.o

b.o:b.c h2.hgcc –c b.c –o b.o

2 、 make 的执行:make (默认使用当前目录下的 makefile )make –f mk (指定编译使用的 make 文件是 mk )

Page 21: Linux 下的 C 程序编程

21

1. 写一个自己的头文件 , 在头文件中定义一个变量和一个函数。

vi my.hint a;int myfun(int x, int y){ int z; z=x*y; return z;}

2. 写一个主文件 , 获得用户输入的两个数,利用自己的函数得到成绩。

vi my.c#include <stdio.h>#include “my.h”int main(){ int x,y,z; printf(“please input two numbers:\

n”); scanf(“%d,%d”,&x,&y); z=myfun(x,y); printf(“x*y=%d”,z); return z;}

3.给自己的程序写一个 makefile 文件vi mymakemy:my.c

gcc –o my.exe –g my.c4.运行,测试,观察结果make –f mymake./my.exeecho $?

Page 22: Linux 下的 C 程序编程

22

第一个目标是最终目标,目标顺序不能颠倒

定义一个功能是删除中间文件特殊目标,命名为 clr通过执行 make clr ,中间文件 to.o会被删除

Page 23: Linux 下的 C 程序编程

23

3 、在 makefile 中使用变量(或称宏)可方便对 makefile 文件进行维护。引用变量时,名字超过一个字符要加圆括号。如ob=test.c test.h

fg=-LS

target:$(ob)

gcc $(ob) $(fg) –o target练习对多个文件写 makefile :可先试着用独立的 g

cc 语句单独编译成功,再把用过的语句组织成 makefile 文件。

Page 24: Linux 下的 C 程序编程

24

* 举例 2 ,猜数字游戏vi guess.h

#include "time.h"#include "stdlib.h"#include "stdio.h"#include "string.h"void guesssub(int i);typedef struct goods{ char name[20]; int price;}GOODS;

vi guessmain.c

#include "guess.h"main(){ GOODS goodslist[5]; int i; strcpy(goodslist[0].name," 钱包 "); goodslist[0].price=288; strcpy(goodslist[1].name," 领带 "); goodslist[1].price=320; strcpy(goodslist[2].name," 手表 "); goodslist[2].price=560; strcpy(goodslist[3].name," 礼帽 "); goodslist[3].price=260; strcpy(goodslist[4].name," 皮带 "); goodslist[4].price=470; do{ printf("\n **小游戏:猜商品价格** \n"); printf("---------------------------\n"); for(i=0;i<5;i++) printf("\t%d---%s\n",i,goodslist[i].name); printf("---------------------------\n"); printf(" 请选择一个上述商品的编号进行输入: "); scanf("%d",&i); guesssub(goodslist[i].price); printf(" 还想继续吗 ?y-- 继续 任意键 -- 结束游

戏: "); getchar(); }while(getchar()=='y');}

vi guesssub.c

#include "guess.h"void guesssub(int i){ char c; time_t a,b; double var; int guess; srand(time(NULL)); a=time(NULL); printf("\n 开始啰 !\n 请输入价格: "); scanf("%d",&guess); while(guess!=i) {if(guess>i) printf(" 高了!请重输价格: "); else printf(" 低了!请重输价格: "); scanf("%d",&guess); } b=time(NULL); var=difftime(b,a);printf("\n\nOK! 所猜商品价格正是 %d 元 \n",i); printf(" 用时 %6.3f 秒 \n\n",var); if(var<20) printf(" **你相当聪明!** \n\n“");else if(var<35) printf(" **你表现一般 ! ** \n\n");else printf(" **你需要提高水平,加油 ! ** \n\n");}

Page 25: Linux 下的 C 程序编程

25

二、 GDB 调试器1 、 GDB介绍GDB 调试器主要用于调试可执行文件,这个文件

必须提供调试版本,包含调试信息。 所以,编译程序时一定用调试选项使生成的可执行文件内包含调试信息,常用 -g 。

调试信息包含程序里每个变量的类型和在可执行文件里的地址映射及行号。

主要功能:监视程序中变量的值 设置断点逐行执行代码

Page 26: Linux 下的 C 程序编程

26

gdb 常用参数列表

运行

r(run) 启动运行程序

c(continue) 继续运行。

断点

b(breakpoint) 行号 在某行加断点

info b 显示所有断点信息d(delete) 断点号 删除断点

单步

n(next) 单步运行下一行代码

s(step) 单步运行下一行代码(函数)

监视

watch 表达式 设置观察点,表达式变化时暂停

Page 27: Linux 下的 C 程序编程

27

2 、使用举例:调试一个计算 1 ~ 50 和 1 ~ 100 累加值的程序。

初始源代码的输入初始源代码的输入

编译带调试信息的可执行文件编译带调试信息的可执行文件

启动 GDB 开始调试启动 GDB 开始调试

运用 GDB 命令,分析源代码执行运用 GDB 命令,分析源代码执行

退出 GDB退出 GDB

用 vi 修改调试中发现的源代码错误用 vi修改调试中发现的源代码错误

Page 28: Linux 下的 C 程序编程

28

( 1 ) vi tst.c ,打开 vi 编辑器,编辑 tst.c 源程序文件

#include <stdio.h>int sum(int n);main(){ int i,result=0; for(i=1;i<=50;i++) result+=i; printf("result[1-50]=%d\n",result); printf("result[1-100]=%d\n",sum(100)); }int sum(int n){ int i ,sum; for(i=1;i<=n;i++) sum+=i; return sum;}

Page 29: Linux 下的 C 程序编程

29

( 2 )编译 tst.c 并运行 tst 程序

编译: gcc tst.c –o tst运行: ./tst

gcc 编译时无报错,表明编译通过。但运行tst 时,第一行结果 result[1-50]=1275 是正确的,第二行结果 result[1-100]=1244812206 与预想不符

Page 30: Linux 下的 C 程序编程

30

( 3 )调试1. 输入命令: gcc -g tst.c -o tstsum2. 启动调试: gdb tstsum

• 在 gdb 下进行调试过程用到许多基本 gdb 命令,一般都只写首字母即可。

• 键入“ l” ( list )查看 gdb 载入文件 tstsm 的源代码。按回车则重复上一次操作。

查看文件

按回车键

按回车键

程序的关键断点。

Page 31: Linux 下的 C 程序编程

31

在第 18 行设置断点

键入” info b”查看设置断点的情况;输入命令“ d 断点号”删除( delete )断点

GDB中键入“ r” ( run ),程序从首行运行到断点前一句,在断点处暂停。

Page 32: Linux 下的 C 程序编程

32

在 GDB中键入“ p ( print ) 变量名”命令查看断点处的相关变量值

在 gdb中输入命令“ n” ( next ),采用单步运行方式继续往下执行程序

在 gdb中输入命令“ c” ( continue )把剩余还未执行的程序执行完键入“ q” ( quit ),退出 GDB 调试,返回终端的系统提示符。

本例子错误在 sum函数中未对 sum变量初始化。

Page 33: Linux 下的 C 程序编程

33

3 、 GDB 基本命令列表命 令 简 写 功 能

help h 查看 GDB命令的帮助信息file 装入需要调试的可执行文件kill k 终止正在调试的程序list l 列出产生执行文件的源代码的一部分next n 执行一行源代码但不进入函数内部step s 执行一行源代码且进入函数内部continue c 继续执行程序,直至下一中断或者程序结束run r 执行当前被调试的程序quit q 终止 GDB

watch 使你能监视一个变量的值而不管它何时被改变catch 布置捕捉点thread t 查看当前运行程序的线程信息break b 在代码中设置断点,这将使程序执行到这里时被挂起make 不退出 GDB而重新产生可执行文件shell 使你能不离开 GDB就执行 Shell命令print p 打印数据内容backtrace bt 查看函数调用栈的所有信息

Page 34: Linux 下的 C 程序编程

34

关于 watch 变量注意, watch 设置的观察点当值有改变时才会有输出。 有时对断点的变量设置了观察点,但是 c ( continue ),

n ( ext )使用的不适当会看不到变量值的变化效果。某个求字符串反序的程序中的一段代码

for(i=0;i<size;i++)

str2[size-i]=str1[i]; ( a )str2[size+1]=‘\0’; ( b )

gdb 调试时,在 a 处设置断点, run 后停在 a 处,设置对变量的观察点 watch str2[size-i] 。然后按 n 一步步执行,看不到值的变化,按 c 才能看到效果。

Page 35: Linux 下的 C 程序编程

35

4 、 GDB 的 help 命令

1. 直接输入 help ,可查看到GDB 命令种类

2. 查看某个类中的各种命令。如上步中列出的种类 data

3. 查看某个命令

(gdb) helpList of classes of commands: aliases -- Aliases of other commandsbreakpoints -- Making program stop at certain pointsdata -- Examining datafiles -- Specifying and examining filesinternals -- Maintenance commands…Type "help" followed by a class name for a list of commands in that class.Type "help" followed by command name for full documentation.Command name abbreViations are allowed if unambiguous.

(gdb) help dataExamining data. List of commands: call -- Call a function in the programdelete display -- Cancel some expressions to be displayed when program stopsdelete mem -- Delete memory regiondisable display -- Disable some expressions to be displayed when program stops…Type "help" followed by command name for full documentation.Command name abbreViations are allowed if unambiguous.

查找 call 命令: help call 。 (gdb) help callCall a function in the program.The argument is the function name and arguments, in the notation of thecurrent working language.  The result is printed and saved in the valuehistory, if it is not void.

Page 36: Linux 下的 C 程序编程

36

实验几个简单 C 程序。课本及实验教材中陆续会出现各种 C 程序。均

可作实验。 出现的函数很多是各种功能的 linux 系统调用,注意多积累对系统调用的认识。

Page 37: Linux 下的 C 程序编程

37

源代码安装 GCC (选看)①下载源代码包,解压缩软件包的归档文件到一个目录下。(利用 tar 命令)

②配置源代码,使其本地化,包括设置安装路径、目标环境(指定编译器)、软件功能裁剪等。(利用配置工具——源码所在目录下的 configure 可执行文件,设置里面的开关参数即可)

③配置后会生成 makefile 文件,它记录了配置的细节和对应的软件编译流程。

④编译。根据 makefile 文件编译后,源代码和可执行文件混合放在安装目录下。(利用源代码工程管理工具—— make )

⑤安装。即将可执行文件和相应的库文件拷贝到目标目录下,并建立相对应的链接文件。(利用 make install 命令)

建立工作目录mkdir ~/mygcccd ~/mygcc

① 在 mygcc 目录下解压源代码包tar –vxjf /tmp/gcc-4.4.0.tar.bz2( 或 tar –vxzf /tmp/gcc-4.4.0.tar.gz)

② 准备一个放编译结果的目录mkdir gccbuildcd gccbuild

③ gccbuild 下配置安装的目标路径~/mygcc/gcc-4.4.0/configure –prefix=/usr/local/gcc-4.4.0

④ 编译 make⑤ 安装 make install

/usr/local/gcc-4.4.0/bin 目录下会出现 gcc 的可执行文件

⑥ 在 bin 目录下测试执行./gcc./gcc –v

⑦ 建立新版本 gcc 的链接文件以防与旧版冲突

ln –s /usr/local/gcc-4.4.0/bin/gcc gcc4⑧ 测试一个 c 语言程序

cd任意目录写一个简单的 c 程序,gcc4 test.c –o test