Linux 下 C 语言编程.docx
《Linux 下 C 语言编程.docx》由会员分享,可在线阅读,更多相关《Linux 下 C 语言编程.docx(19页珍藏版)》请在冰豆网上搜索。
![Linux 下 C 语言编程.docx](https://file1.bdocx.com/fileroot1/2023-1/9/18820aca-5620-4f75-8d05-ffed56870bb7/18820aca-5620-4f75-8d05-ffed56870bb71.gif)
Linux下C语言编程
Linux下C语言编程
前言
Linux的发行版中包含了很多软件开发工具.它们中的很多是用于C和C++应用程序开
发的.本文介绍了在Linux下能用于C应用程序开发和调试的工具.本文的主旨是介
绍如何在Linux下使用C编译器和其他C编程工具,而非C语言编程的教程.在本
文中你将学到以下知识:
什么是C
GNUC编译器
用gdb来调试GCC应用程序
你也能看到随Linux发行的其他有用的C编程工具.这些工具包括源程序美化程
序(prettyprintprograms),附加的调试工具,函数原型自动生成工具(automatic
functionprototypers).
----------------------------------------------------------------------------
----
注意:
源程序美化程序(prettyprintprograms)自动帮你格式化源代码产生始终如一
的缩进格式.
----------------------------------------------------------------------------
----
什么是C?
C是一种在UNIX操作系统的早期就被广泛使用的通用编程语言.它最早是由贝尔
实验室的DennisRitchie为了UNIX的辅助开发而写的,开始时UNIX是用汇编语言
和一种叫B的语言编写的.从那时候起,C就成为世界上使用最广泛计算机语言.
C能在编程领域里得到如此广泛支持的原因有以下一些:
它是一种非常通用的语言.几乎你所能想到的任何一种计算机上都有至少一种能用的C
编译器.并且它的语法和函数库在不同的平台上都是统一的,这个特性对开发者来说很
有吸引力.
用C写的程序执行速度很快.
C是所有版本的UNIX上的系统语言.
C在过去的二十年中有了很大的发展.在80年代末期美国国家标准协会(American
NationalStandardsInstitute)发布了一个被称为ANSIC的C语言标准.这更加保
证了将来在不同平台上的C的一致性.在80年代还出现了一种C的面向对象的扩展称
为C++.C++将在另一篇文章"C++编程"中描述.
Linux上可用的C编译器是GNUC编译器,它建立在自由软件基金会的编程许可
证的基础上,因此可以自由发布.你能在Linux的发行光盘上找到它.
GNUC编译器
随SlackwareLinux发行的GNUC编译器(GCC)是一个全功能的ANSIC兼容编
译器.如果你熟悉其他操作系统或硬件平台上的一种C编译器,你将能很快地掌握
GCC.本节将介绍如何使用GCC和一些GCC编译器最常用的选项.
使用GCC
通常后跟一些选项和文件名来使用GCC编译器.gcc命令的基本用法如下:
gcc[options][filenames]
命令行选项指定的操作将在命令行上每个给出的文件上执行.下一小节将叙述一些
你会最常用到的选项.
GCC选项
GCC有超过100个的编译选项可用.这些选项中的许多你可能永远都不会用到,但
一些主要的选项将会频繁用到.很多的GCC选项包括一个以上的字符.因此你必须为
每个选项指定各自的连字符,并且就象大多数Linux命令一样你不能在一个单独的连
字符后跟一组选项.例如,下面的两个命令是不同的:
gcc-p-gtest.c
gcc-pgtest.c
第一条命令告诉GCC编译test.c时为prof命令建立剖析(profile)信息并且把
调试信息加入到可执行的文件里.第二条命令只告诉GCC为gprof命令建立剖析信息
.
当你不用任何选项编译一个程序时,GCC将会建立(假定编译成功)一个名为a.out
的可执行文件.例如,下面的命令将在当前目录下产生一个叫a.out的文件:
gcctest.c
你能用-o编译选项来为将产生的可执行文件指定一个文件名来代替a.out.例如
将一个叫count.c的C程序编译为名叫count的可执行文件,你将输入下面的命
令:
gcc-ocountcount.c
----------------------------------------------------------------------------
----
注意:
当你使用-o选项时,-o后面必须跟一个文件名.
----------------------------------------------------------------------------
----
GCC同样有指定编译器处理多少的编译选项.-c选项告诉GCC仅把源代码编译为
目标代码而跳过汇编和连接的步骤.这个选项使用的非常频繁因为它使得编译多个C
程序时速度更快并且更易于管理.缺省时GCC建立的目标代码文件有一个.o的扩展
名.
-S编译选项告诉GCC在为C代码产生了汇编语言文件后停止编译.GCC产生的
汇编语言文件的缺省扩展名是.s.-E选项指示编译器仅对输入文件进行预处理.当
这个选项被使用时,预处理器的输出被送到标准输出而不是储存在文件里.
优化选项
当你用GCC编译C代码时,它会试着用最少的时间完成编译并且使编译后的代码
易于调试.易于调试意味着编译后的代码与源代码有同样的执行次序,编译后的代码没
有经过优化.有很多选项可用于告诉GCC在耗费更多编译时间和牺牲易调试性的基础
上产生更小更快的可执行文件.这些选项中最典型的是-O和-O2选项.
-O选项告诉GCC对源代码进行基本优化.这些优化在大多数情况下都会使程序执
行的更快.-O2选项告诉GCC产生尽可能小和尽可能快的代码.-O2选项将使编译的
速度比使用-O时慢.但通常产生的代码执行速度会更快.
除了-O和-O2优化选项外,还有一些低级选项用于产生更快的代码.这些选项
非常的特殊,而且最好只有当你完全理解这些选项将会对编译后的代码产生什么样的效
果时再去使用.这些选项的详细描述,请参考GCC的指南页,在命令行上键入man
gcc.
调试和剖析选项
GCC支持数种调试和剖析选项.在这些选项里你会最常用到的是-g和-pg选项.
-g选项告诉GCC产生能被GNU调试器使用的调试信息以便调试你的程序.GCC
提供了一个很多其他C编译器里没有的特性,在GCC里你能使-g和-O(产生优化
代码)联用.这一点非常有用因为你能在与最终产品尽可能相近的情况下调试你的代码.
在你同时使用这两个选项时你必须清楚你所写的某些代码已经在优化时被GCC作了改
动.关于调试C程序的更多信息请看下一节"用gdb调试C程序".
-pg选项告诉GCC在你的程序里加入额外的代码,执行时,产生gprof用的剖析
信息以显示你的程序的耗时情况.关于gprof的更多信息请参考"gprof"一节.
用gdb调试GCC程序
Linux包含了一个叫gdb的GNU调试程序.gdb是一个用来调试C和C++程序
的强力调试器.它使你能在程序运行时观察程序的内部结构和内存的使用情况.以下是
gdb所提供的一些功能:
它使你能监视你程序中变量的值.
它使你能设置断点以使程序在指定的代码行上停止执行.
它使你能一行行的执行你的代码.
在命令行上键入gdb并按回车键就可以运行gdb了,如果一切正常的话,gdb将
被启动并且你将在屏幕上看到类似的内容:
GDBisfreesoftwareandyouarewelcometodistributecopiesofit
undercertainconditions;type"showcopying"toseetheconditions.
ThereisabsolutelynowarrantyforGDB;type"showwarranty"fordetails.
GDB4.14(i486-slakware-linux),Copyright1995FreeSoftwareFoundation,
Inc.
(gdb)
当你启动gdb后,你能在命令行上指定很多的选项.你也可以以下面的方式来运
行gdb:
gdb
当你用这种方式运行gdb,你能直接指定想要调试的程序.这将告诉gdb装入名
为fname的可执行文件.你也可以用gdb去检查一个因程序异常终止而产生的core
文件,或者与一个正在运行的程序相连.你可以参考gdb指南页或在命令行上键入
gdb-h得到一个有关这些选项的说明的简单列表.
为调试编译代码(CompilingCodeforDebugging)
为了使gdb正常工作,你必须使你的程序在编译时包含调试信息.调试信息包含
你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号.gdb利
用这些信息使源代码和机器码相关联.
在编译时用-g选项打开调试选项.
gdb基本命令
gdb支持很多的命令使你能实现不同的功能.这些命令从简单的文件装入到允许
你检查所调用的堆栈内容的复杂命令,表27.1列出了你在用gdb调试时会用到的一些
命令.想了解gdb的详细使用请参考gdb的指南页.
表27.1.基本gdb命令.
命令描述
file装入想要调试的可执行文件.
kill终止正在调试的程序.
list列出产生执行文件的源代码的一部分.
next执行一行源代码但不进入函数内部.
step执行一行源代码而且进入函数内部.
run执行当前被调试的程序
quit终止gdb
watch使你能监视一个变量的值而不管它何时被改变.
break在代码里设置断点,这将使程序执行到这里时被挂起.
make使你能不退出gdb就可以重新产生可执行文件.
shell使你能不离开gdb就执行UNIXshell命令.
gdb支持很多与UNIXshell程序一样的命令编辑特征.你能象在bash或tcsh
里那样按Tab键让gdb帮你补齐一个唯一的命令,如果不唯一的话gdb会列出所有
匹配的命令.你也能用光标键上下翻动历史命令.
gdb应用举例
本节用一个实例教你一步步的用gdb调试程序.被调试的程序相当的简单,但它
展示了gdb的典型应用.
下面列出了将被调试的程序.这个程序被称为greeting,它显示一个简单的问候
再用反序将它列出.
#include
main()
{
charmy_string[]="hellothere";
my_print(my_string);
my_print2(my_string);
}
voidmy_print(char*string)
{
printf("Thestringis%s\n",string);
}
voidmy_print2(char*string)
{
char*string2;
intsize,i;
size=strlen(string);
string2=(char*)malloc(size+1);
for(i=0;istring2[size-i]=string[i];
string2[size+1]=`\0';
printf("Thestringprintedbackwardis%s\n",string2);
}
用下面的命令编译它:
gcc-otesttest.c
这个程序执行时显示如下结果:
Thestringishellothere
Thestringprintedbackwardis
输出的第一行是正确的,但第二行打印出的东西并不是我们所期望的.我们所设想
的输出应该是:
Thestringprintedbackwardiserehtolleh
由于某些原因,my_print2函数没有正常工作.让我们用gdb看看问题究竟出在
哪儿,先键入如下命令:
gdbgreeting
----------------------------------------------------------------------------
----
注意:
记得在编译greeting程序时把调试选项打开.
----------------------------------------------------------------------------
----
如果你在输入命令时忘了把要调试的程序作为参数传给gdb,你可以在gdb提示
符下用file命令来载入它:
(gdb)filegreeting
这个命令将载入greeting可执行文件就象你在gdb命令行里装入它一样.
这时你能用gdb的run命令来运行greeting了.当它在gdb里被运行后结果
大约会象这样:
(gdb)run
Startingprogram:
/root/greeting
Thestringishellothere
Thestringprintedbackwardis
Programexitedwithcode041
这个输出和在gdb外面运行的结果一样.问题是,为什么反序打印没有工作?
为
了找出症结所在,我们可以在my_print2函数的for语句后设一个断点,具体的做法
是在gdb提示符下键入list命令三次,列出源代码:
(gdb)list
(gdb)list
(gdb)list
----------------------------------------------------------------------------
----
技巧:
在gdb提示符下按回车健将重复上一个命令.
----------------------------------------------------------------------------
----
第一次键入list命令的输出如下:
1#include
2
3main()
4{
5charmy_string[]="hellothere";
6
7my_print(my_string);
8my_print2(my_string);
9}
10
如果按下回车,gdb将再执行一次list命令,给出下列输出:
11my_print(char*string)
12{
13printf("Thestringis%s\n",string);
14}
15
16my_print2(char*string)
17{
18char*string2;
19intsize,i;
20
再按一次回车将列出greeting程序的剩余部分:
21size=strlen(string);
22string2=(char*)malloc(size+1);
23for(i=0;i24string2[size-i]=string[i];
25string2[size+1]=`\0';
26printf("Thestringprintedbackwardis%s\n",string2);
27}
根据列出的源程序,你能看到要设断点的地方在第24行,在gdb命令行提示符下
键入如下命令设置断点:
(gdb)break24
gdb将作出如下的响应:
Breakpoint1at0x139:
filegreeting.c,line24
(gdb)
现在再键入run命令,将产生如下的输出:
Startingprogram:
/root/greeting
Thestringishellothere
Breakpoint1,my_print2(string=0xbfffdc4"hellothere")atgreeting.c:
24
24string2[size-i]=string[i]
你能通过设置一个观察string2[size-i]变量的值的观察点来看出错误是怎样
产生的,做法是键入:
(gdb)watchstring2[size-i]
gdb将作出如下回应:
Watchpoint2:
string2[size-i]
现在可以用next命令来一步步的执行for循环了:
(gdb)next
经过第一次循环后,gdb告诉我们string2[size-i]的值是`h`.gdb用如下
的显示来告诉你这个信息:
Watchpoint2,string2[size-i]
Oldvalue=0`\000'
Newvalue=104`h'
my_print2(string=0xbfffdc4"hellothere")atgreeting.c:
23
23for(i=0;i这个值正是期望的.后来的数次循环的结果都是正确的.当i=10时,表达式
string2[size-i]的值等于`e`,size-i的值等于1,最后一个字符已经拷到新
串里了.
如果你再把循环执行下去,你会看到已经没有值分配给string2[0]了,而它是
新串的第一个字符,因为malloc函数在分配内存时把它们初始化为空(null)字符.所
以string2的第一个字符是空字符.这解释了为什么在打印string2时没有任何输出
了.
现在找出了问题出在哪里,修正这个错误是很容易的.你得把代码里写入string2
的第一个字符的的偏移量改为size-1而不是size.这是因为string2的大小为
12,但起始偏移量是0,串内的字符从偏移量0到偏移量10,偏移量11为空字符
保留.
为了使代码正常工作有很多种修改办法.一种是另设一个比串的实际大小小1的
变量.这是这种解决办法的代码:
#include
main()
{
charmy_string[]="hellothere";
my_print(my_string);
my_print2(my_string);
}
my_print(char*string)
{
printf("Thestringis%s\n",string);
}
my_print2(char*string)
{
char*string2;
intsize,size2,i;
size=strlen(string);
size2=size-1;
string2=(char*)malloc(size+1);
for(i=0;istring2[size2-i]=string[i];
string2[size]=`\0';
printf("Thestringprintedbackwardis%s\n",string2);
}
另外的C编程工具
SlackwareLinux的发行版中还包括一些我们尚未提到的C开发工具.本节将介
绍这些工具和它们的典型用法.
xxgdb
xxgdb是gdb的一个基于XWindow系统的图形界面.xxgdb包括了命令行版的
gdb上的所有特性.xxgdb使你能通过按按钮来执行常用的命令.设置了断点的地方
也用图形来显示.
你能在一个Xterm窗口里键入下面的命令来运行它:
xxgdb
你能用gdb里任何有效的命令行选项来初始化xxgdb.此外xxgdb也有一些特
有的命令行选项,表27.2列出了这些选项.
表27.2.xxgdb命令行选项.
选项描述
db_name指定所用调试器的名字,缺省是gdb.
db_prompt指定调试器提示符,缺省为gdb.
gdbinit指定初始化gdb的命令文件的文件名,缺省为.gdbinit.
nx告诉xxgdb不执行.gdbinit文件.
bigicon使用大图标.
calls
你可以在sunsite.unc.eduFTP站点用下面的路径:
/pub/Linux/devel/lang/c/calls.tar.Z
来取得calls,一些旧版本的LinuxCD-ROM发行版里也附带有.因为它是一个
有用的工具,我们在这里也介绍一下.如果你觉得有用的话,从BBS,FTP,或另一张
CD-ROM上弄一个拷贝.calls调用GCC的预处理器来处理给出的源程序文件,然后
输出这些文件的里的函数调用树图.
----------------------------------------------------------------------------
----
注意:
在你的系统上安装calls,以超级用户身份登录后执行下面的步骤:
1.解压和
untar文件.2.cd进入callsuntar后建立的子目录.3.把名叫calls的文件移
动到/usr/bin目录.4.把名叫calls.1的文件移动到目录/usr/man/man1.5.删
除/tmp/calls目录.这些步骤将把calls程序和它的指南页安装载你的系统上.
----------------------------------------------------------------------------
----
当calls打印出调用跟踪结果时,它在函数后面用中括号给出了函数所在文件的