linux下C编程.docx
- 文档编号:9584760
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:32
- 大小:301KB
linux下C编程.docx
《linux下C编程.docx》由会员分享,可在线阅读,更多相关《linux下C编程.docx(32页珍藏版)》请在冰豆网上搜索。
linux下C编程
Linux下的C编程基础
一、Linux下C语言编程概述
1、特点:
C语言是“中级语言”。
C语言是结构化的语言。
C语言功能齐全。
C语言可移植性强。
C语言适合多种操作系统,如DOS、Windows、Linux,也适合多种体系结构,因此尤其适合在嵌入式领域的开发。
2、Linux下C语言编程环境概述
Linux下的C语言程序设计与在其他环境中的C程序设计一样,主要涉及到编辑器、编译链接器、调试器及项目管理工具。
(1)编辑器
Linux中最常用的编辑器有Vi(Vim)
图1编译过程
(2)编译链接器
编译是指源代码转化生成可执行代码的过程,它所完成工作主要如图1所示。
在Linux中,最常用的编译器是Gcc编译器。
(3)调试器
Gdb是绝大多数Linux开发人员所使用的调试器,它可以方便地设置断点、单步跟踪等,足以满足开发人员的需要。
(4)项目管理器
Linux中的项目管理器“make”有些类似于Windows中VisualC++里的“工程”,它是一种控制编译或者重复编译软件的工具,另外,它还能自动管理软件编译的内容、方式和时机,使程序员能够把精力集中在代码的编写上而不是在源代码的组织上。
二、进入Vi(vi文件名)
1、Vi的模式
Vi有3种模式,分别为命令行模式、插入模式及底行模式能,下面具体进行介绍。
(1)命令行模式
用户在用Vi编辑文件时,最初进入的为一般模式。
在该模式中可以通过上下移动光标进行“删除字符”或“整行删除”等操作,也可以进行“复制”、“粘贴”等操作,但无法编辑文字。
(2)插入模式
只有在该模式下,用户才能进行文字编辑输入,用户按[ESC]键回到命令行模式。
(3)底行模式
在该模式下,光标位于屏幕的底行。
用户可以进行文件保存或退出操作,也可以设置编辑环境,如寻找字符串、列出行号等。
2、Vi的基本流程
(1)进入Vi,即在命令行下键入Vihello(文件名)。
此时进入的是命令行模式,光标位于屏幕的上方,如图2所示。
(2)在命令行模式下键入i进入到插入模式,如图3所示。
可以看出,在屏幕底部显示有“插入”表示插入模式,在该模式下可以输入文字信息。
图2进入Vi命令行模式
图3进入Vi插入模式
(3)最后,在插入模式中,输入“Esc”,则当前模式转入命令行模式,并在底行行中输入“:
wq”(存盘退出)进入底行模式,如图4所示。
这样,就完成了一个简单的Vi操作流程:
命令行模式→插入模式→底行模式。
由于Vi在不同的模式下有不同的操作功能,因此,读者一定要时刻注意屏幕最下方的提示,分清所在的模式。
图4进入Vi底行模式
3、Vi的各模式功能键
(1)命令行模式常见功能键如表1所示。
表1Vi命令行模式功能键
(2)插入模式的功能键只有一个,也就是Esc退出到命令行模式。
(3)底行模式常见功能键如表2所示。
表2Vi底行模式功能键
三、Gcc编译器
GNUCC(简称为Gcc)是GNU项目中符合ANSIC标准的编译系统,能够编译用C、C++和ObjectC等语言编写的程序。
Gcc不仅功能强大,而且可以编译如C、C++、ObjectC、Java、Fortran、Pascal、Modula-3和Ada等多种语言,而且Gcc又是一个交叉平台编译器,它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。
Gcc版本(gcc–v)。
下表3是Gcc支持编译源文件的后缀及其解释。
表3Gcc所支持后缀名解释
1、Gcc编译流程解析
Gcc的编译流程分为了4个步骤,分别为:
∙预处理(Pre-Processing);
∙编译(Compiling);
∙汇编(Assembling);
∙链接(Linking)。
首先,有以下hello.c源代码:
#include
intmain()
{
printf("Hello!
Thisisourembeddedworld!
\n");
return0;
}
(1)预处理阶段
在该阶段,编译器将上述代码中的stdio.h编译进来,并且用户可以使用Gcc的选项“-E”进行查看,该选项的作用是让Gcc在预处理结束后停止编译过程。
注意Gcc指令的一般格式为:
Gcc[选项]要编译的文件[选项][目标文件]
其中,目标文件可缺省,Gcc默认生成可执行的文件,命为:
编译文件.out
[root@localhostGcc]#Gcc–Ehello.c–ohello.i
在此处,选项“-o”是指目标文件,由表3可知,“.i”文件为已经过预处理的C原始程序。
以下列出了hello.i文件的部分内容:
typedefint(*__gconv_trans_fct)(struct__gconv_step*,
struct__gconv_step_data*,void*,
__constunsignedchar*,
__constunsignedchar**,
__constunsignedchar*,unsignedchar**,
size_t*);
…
#2"hello.c"2
intmain()
{
printf("Hello!
Thisisourembeddedworld!
\n");
return0;
}
由此可见,Gcc确实进行了预处理,它把“stdio.h”的内容插入到hello.i文件中。
(2)编译阶段
接下来进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。
用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
[root@localhostGcc]#Gcc–Shello.i–ohello.s
以下列出了hello.s的内容,可见Gcc已经将其转化为汇编了。
.file"hello.c"
.section.rodata
.align4
.LC0:
.string"Hello!
Thisisourembeddedworld!
"
.text
.globlmain
.typemain,@function
main:
pushl%ebp
movl%esp,%ebp
subl$8,%esp
andl$-16,%esp
movl$0,%eax
addl$15,%eax
addl$15,%eax
shrl$4,%eax
sall$4,%eax
subl%eax,%esp
subl$12,%esp
pushl$.LC0
callputs
addl$16,%esp
movl$0,%eax
leave
ret
.sizemain,.-main
.ident"GCC:
(GNU)4.0.020050519(RedHat4.0.0-8)"
.section.note.GNU-stack,"",@progbits
(3)汇编阶段
汇编阶段是把编译阶段生成的“.s”文件转成目标文件,读者在此可使用选项“-c”就可
看到汇编代码已转化为“.o”的二进制目标代码了。
如下所示:
[root@localhostGcc]#Gcc–chello.s–ohello.o
(4)链接阶段
在成功编译之后,就进入了链接阶段。
在这里涉及到一个重要的概念:
函数库。
读者可以重新查看这个小程序,在这个程序中并没有定义“printf”的函数实现,且在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现“printf”函数的呢?
最后的答案是:
系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,Gcc会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。
函数库一般分为静态库和动态库两种。
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。
其后缀名一般为“.a”。
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由链接文件加载库,这样可以节省系统的开销。
动态库一般后缀名为“.so”,如前面所述的libc.so.6就是动态库。
Gcc在编译时默认使用动态库。
完成了链接之后,Gcc就可以生成可执行文件,如下所示。
[root@localhostGcc]#Gcchello.o–ohello
运行该可执行文件,出现正确的结果如下。
[root@localhostGcc]#./hello
Hello!
Thisisourembeddedworld!
2、Gcc编译选项分析
Gcc有超过100个的可用选项,主要包括总体选项、告警和出错选项、优化选项和体系结构相关选项。
以下对每一类中最常用的选项进行讲解。
(1)总体选项
Gcc的总结选项如表4所示,很多在前面的示例中已经有所涉及。
表4Gcc总体选项列表
对于“-c”、“-E”、“-o”、“-S”选项在前一小节中已经讲解了其使用方法,在此主要讲解另外两个非常常用的库依赖选项“-Idir”和“-Ldir”。
∙“-Idir”
正如上表中所述,“-Idir”选项可以在头文件的搜索路径列表中添加dir目录。
由于Linux中头文件都默认放到了“/usr/include/”目录下,因此,当用户希望添加放置在其他位置的头文件时,就可以通过“-Idir”选项来指定,这样,Gcc就会到相应的位置查找对应的目录。
比如在“/root/workplace/Gcc”下有两个文件:
/*hello1.c*/
#include
intmain()
{
printf("Hello!
!
\n");
return0;
}
/*my.h*/
#include
这样,就可在Gcc命令行中加入“-I”选项:
[root@localhostGcc]Gcchello1.c–I/root/workplace/Gcc/-ohello1
这样,Gcc就能够执行出正确结果。
小知识在include语句中,“<>”表示在标准路径中搜索头文件,““””表示在本目录中搜索。
故在上例中,可把hello1.c的“#include
∙“-Ldir”
选项“-Ldir”的功能与“-Idir”类似,能够在库文件的搜索路径列表中添加dir目录。
例如有程序hello_sq.c需要用到目录“/root/workplace/Gcc/lib”下的一个动态库libateng.so,则只需键入如下命令即可:
[root@localhostGcc]Gcchello_sq.c–L/root/workplace/Gcc/lib–lateng–ohello_sq
需要注意的是,“-Idir”和“-Ldir”都只是指定了路径,而没有指定文件,因此不能在路径中包含文件名。
另外值得详细解释一下的是“-l”选项,它指示Gcc去连接库文件libateng.so。
由于在Linux下的库文件命名时有一个规定:
必须以l、i、b3个字母开头。
因此在用-l选项指定链接的库文件名时可以省去l、i、b3个字母。
也就是说Gcc在对“-lateng”进行处理时,会自动去链接名为libateng.so的文件。
(2)告警和出错选项
Gcc的告警和出错选项如表5所示。
如有以下程序段:
#include
voidmain()
{
longlongtmp=1;
printf("Thisisabadcode!
\n");
return0;
}
这是一个很糟糕的程序,读者可以考虑一下有哪些问题?
∙“-ansi”
该选项强制Gcc生成标准语法所要求的告警信息,尽管这还并不能保证所有没有警告的程序都是符合ANSIC标准的。
运行结果如下所示:
[root@localhostGcc]#Gcc-ansiwarning.c–owarning
warning.c:
在函数“main”中:
warning.c:
7警告:
在无返回值的函数中,“return”带返回值
warning.c:
4警告:
“main”的返回类型不是“int”
可以看出,该选项并没有发现“longlong”这个无效数据类型的错误。
∙“-pedantic”
允许发出ANSIC标准所列的全部警告信息,同样也保证所有没有警告的程序都是符合ANSIC标准的。
其运行结果如下所示:
[root@localhostGcc]#Gcc–pedanticwarning.c–owarning
warning.c:
在函数“main”中:
warning.c:
5警告:
ISOC90不支持“longlong”
warning.c:
7警告:
在无返回值的函数中,“return”带返回值
warning.c:
4警告:
“main”的返回类型不是“int”
可以看出,使用该选项查看出了“longlong”这个无效数据类型的错误。
∙“-Wall”
允许发出Gcc能够提供的所有有用的报警信息。
该选项的运行结果如下所示:
[root@localhostGcc]#Gcc–Wallwarning.c–owarning
warning.c:
4警告:
“main”的返回类型不是“int”
warning.c:
在函数“main”中:
warning.c:
7警告:
在无返回值的函数中,“return”带返回值
warning.c:
5警告:
未使用的变量“tmp”
使用“-Wall”选项找出了未使用的变量tmp,但它并没有找出无效数据类型的错误。
另外,Gcc还可以利用选项对单独的常见错误分别指定警告,有关具体选项的含义感兴趣的读者可以查看Gcc手册进行学习。
(3)优化选项
Gcc可以对代码进行优化,它通过编译选项“-On”来控制优化代码的生成,其中n是一个代表优化级别的整数。
对于不同版本的Gcc来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。
(4)体系结构相关选项
Gcc的体系结构相关选项如表6所示。
表6Gcc体系结构相关选项列表
四、Gdb调试器
Gdb调试器是一款GNU开发组织并发布的UNIX/Linux下的程序调试工具。
虽然,它没有图形化的友好界面,但是它强大的功能也足以与微软的VC工具等媲美。
1、Gdb使用流程
这里给出了一个短小的程序,由此带领读者熟悉一下Gdb的使用流程。
/*test.c*/
#include
intsum(intm);
intmain()
{
inti,n=0;
sum(50);
for(i=1;i<=50;i++)
{
n+=i;
}
printf("Thesumof1-50is%d\n",n);
}
intsum(intm)
{
inti,n=0;
for(i=1;i<=m;i++)
n+=i;
printf("Thesumof1-mis%d\n",n);
}
在保存退出后首先使用Gcc对test.c进行编译,注意一定要加上选项“-g”,这样编译出的可执行代码中才包含调试信息,否则之后Gdb无法载入该可执行文件。
[root@localhostGdb]#gcc-gtest.c-otest
虽然这段程序没有错误,但调试完全正确的程序可以更加了解Gdb的使用流程。
接下来就启动Gdb进行调试。
注意,Gdb进行调试的是可执行文件,而不是如“.c”的源代码,因此,需要先通过Gcc编译生成可执行文件才能用Gdb进行调试。
[root@localhostGdb]#gdbtest
GNUGdbRedHatLinux(6.3.0.0-1.21rh)
Copyright2004FreeSoftwareFoundation,Inc.
GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouare
welcometochangeitand/ordistributecopiesofitundercertainconditions.
Type"showcopying"toseetheconditions.
ThereisabsolutelynowarrantyforGDB.Type"showwarranty"fordetails.
ThisGDBwasconfiguredas"i386-redhat-linux-gnu"...Usinghostlibthread_db
library"/lib/libthread_db.so.1".
(gdb)
接下来就进入了由“(gdb)”开头的命令行界面了。
(1)查看文件
在Gdb中键入“l”(list)就可以查看所载入的文件,如下所示:
注意:
在Gdb的命令中都可使用缩略形式的命令,如“l”代便“list”,“b”代表“breakpoint”,“p”代表“print”等,读者也可使用“help”命令查看帮助信息。
(Gdb)l
1#include
2intsum(intm);
3intmain()
4{
5inti,n=0;
6sum(50);
7for(i=1;i<=50;i++)
8{
9n+=i;
10}
(Gdb)l
11printf("Thesumof1~50is%d\n",n);
12
13}
14intsum(intm)
15{
16inti,n=0;
17for(i=1;i<=m;i++)
18n+=i;
19printf("Thesumof1~mis=%d\n",n);
20}
2)设置断点
在Gdb中设置断点非常简单,只需在“b”后加入对应的行号即可。
如下所示:
(Gdb)b6
Breakpoint1at0x804846d:
filetest.c,line6.
要注意的是,在Gdb中利用行号设置断点是指代码运行到对应行之前将其停止,如上例中,代码运行到第5行之前暂停(并没有运行第5行)。
(3)查看断点情况
在设置完断点之后,用户可以键入“infob”来查看设置断点情况,在Gdb中可以设置多个断点。
(Gdb)infob
NumTypeDispEnbAddressWhat
1breakpointkeepy0x0804846dinmainattest.c:
6
(4)运行代码
接下来就可运行代码了,Gdb默认从首行开始运行代码,可键入“r”(run)即可(若想从程序中指定行开始运行,可在r后面加上行号)。
(Gdb)r
Startingprogram:
/root/workplace/Gdb/test
Readingsymbolsfromsharedobjectreadfromtargetmemory...done.
LoadedsystemsuppliedDSOat0x5fb000
Breakpoint1,main()attest.c:
6
6sum(50);
可以看到,程序运行到断点处就停止了。
(5)查看变量值
在程序停止运行之后,程序员所要做的工作是查看断点处的相关变量值。
在Gdb中只需键入“p”+变量值即可,如下所示:
(Gdb)pn
$1=0
(Gdb)pi
$2=134518440
在此处,为什么变量“i”的值为如此奇怪的一个数字呢?
原因就在于程序是在断点设置的对应行之前停止的,那么在此时,并没有把“i”的数值赋为零,而只是一个随机的数字。
但变量“n”是在第四行赋值的,故在此时已经为零。
小技巧:
Gdb在显示变量值时都会在对应值之前加上“$N”标记,它是当前变量值的引用标记,所以以后若想再次引用此变量就可以直接写作“$N”,而无需写冗长的变量名。
(6)单步运行
单步运行可以使用命令“n”(next)或“s”(step),它们之间的区别在于:
若有函数调用的时候,“s”会进入该函数而“n”不会进入该函数。
因此,“s”就类似于VC等工具中的“stepin”,“n”类似与VC等工具中的“stepover”。
它们的使用如下所示:
(Gdb)n
Thesumof1-mis1275
7for(i=1;i<=50;i++)
(Gdb)s
sum(m=50)attest.c:
16
16inti,n=0;
可见,使用“n”后,程序显示函数sum的运行结果并向下执行,而使用“s”后则进入到sum函数之中单步运行。
(7)恢复程序运行
在查看完所需变量及堆栈情况后,就可以使用命令“c”(continue)恢复程序的正常运行了。
这时,它会把剩余还未执行的程序执行完,并显示剩余程序中的执行结果。
以下是之前使用“n”命令恢复后的执行结果:
(Gdb)c
Continuing.
Thesumof1-50is:
1275
Programexitedwithcode031.
可以看出,程序在运行完后退出,之后程序处于“停止状态”。
小知识在Gdb中,程序的运行状态有“运行”、“暂停”和“停止”3种,其中“暂停”状态为程序遇到了断点或观察点之类的,程序暂时停止运行,而此时函数的地址、函数参数、函数内的局部变量都会被压入“栈”(Stack)中。
故在这种状态下可以查看函数的变量值等各种属性。
但在函数处于“停止”状态之后,“栈”就会自动撤销,它也就无法查看各种信息了。
4.4.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 编程