Linux操作系统下C语言编程从零开始.docx
《Linux操作系统下C语言编程从零开始.docx》由会员分享,可在线阅读,更多相关《Linux操作系统下C语言编程从零开始.docx(28页珍藏版)》请在冰豆网上搜索。
Linux操作系统下C语言编程从零开始
Linux操作系统下C语言编程从零开始
时间:
2006-11-2011:
52:
12来源:
Linux联盟收集作者:
这里向大家介绍一下在Linux/UNIX的机器上,进行C/C++编程的一些入门级知识。
·所需具备的背景知识
·开发所需的基本环境
·获得帮助的途径
·通过一个实例了解基本步骤
Prerequisite先决条件:
在Linux上编写C程序,至少要熟悉以下两方面的基础知识:
1.C语言的编程基础,至少要知道一些基本的语法,控制流程等编程常识。
对常用的标准C函数库有常识性的了解。
2.对Linux/UNIX的操作有常识性的了解,掌握常用的shell命令,如ls,cat,cp,mkdir…etc。
Environment所需环境:
1.Linux/Unix的操作系统,也可以使用windows下的cygwin。
我们这里讨论的都是通过shell命令行进行操作的.那如果进入了图形界面的Linux怎么办呢?
只要打开一个终端命令,就和命令行环境完全一样了(打开开始菜单可以找到终端命令)。
2.必备的开发工具:
1)输入程序需要一个编辑器。
常用的有vi,emacs。
在命令行上输入vi,emacs,…就可进入编辑环境
关于vi
关于EMACS
2)C语言的编译器。
常用的是GNU的c语言编译器gcc(编译C程序),g++(编译C++程序)。
关于gcc/g++
关于makefile〉>〉>用于简化编译过程
这里有一片入门文章Linux下C语言编程基础知识,可以先看一下
3)调试程序的常用工具:
gdb。
关于gdb
Gethelp获得帮助:
关于Linux的文档是非常丰富的。
最快捷,方便,全面的资料就在你的机器里,不要浪费。
在命令行上输入shell命令man或者info:
$mangcc〉>〉〉这个命令可以获得GNU的C语言编译器的文档。
当然,他们是英文的.
关于man
关于info
网络上的资源也很多,多得以至于不知道什么才是自己最需要的。
关于如何获得有价值的信息
看一下loveunix上的相关资源>〉>>
Basicsteps基本步骤:
1.输入源代码
2.编译,链接,运行
3.调试程序
我们从最基本的helloworld程序开始,实际操作一下:
1.输入源代码
引用
$emacshello。
c〉>>>进入emacs编辑器环境
#include>〉>如果你看不懂这个函数,就去好好的看c语言的书
intmain()
{
printf(“HelloWorld.\n”);>〉>〉Emacs环境下,按下Tab键,有自动缩进功能
exit(0);
}
完成输入后,按住CTRL键,按下x,再按下c,最后松开CTRL。
〉>〉>程序保留并退出emacs环境。
2.编译,链接,运行
引用
$gcc–ohellohello.c
$。
/hello〉〉〉〉./指明了所执行程序的路径
HelloWorld。
$
一个linux平台上的c程序开发已经完成咯
3.调试
如果要使用gdb调试程序,那么在上一步编译的时候,记得加上–g选项
引用
$gcc–g–ohellohello。
c
$gdbhello〉〉〉>进入gdb调试环境
4。
CVS版本控制软件——协同工作和保留版本的工具
Linux下C语言编程
时间:
2006—06—1118:
33:
00来源:
Linux联盟收集作者:
Linux联盟收集
Linux的发行版中包含了很多软件开发工具。
它们中的很多是用于C和C++应用程序开发的。
本文介绍了在Linux下能用于C应用程序开发和调试的工具。
本文的主旨是介绍如何在Linux下使用C编译器和其他C编程工具,而非C语言编程的教程.在本文中你将学到以下知识:
·什么是C
·GNUC编译器
·用gdb来调试GCC应用程序
你也能看到随Linux发行的其他有用的C编程工具.这些工具包括源程序美化程序(prettyprintprograms),附加的调试工具,函数原型自动生成工具(automaticfunctionprototypers).
注意:
源程序美化程序(prettyprintprograms)自动帮你格式化源代码产生始终如一的缩进格式.
什么是C?
C是一种在UNIX操作系统的早期就被广泛使用的通用编程语言。
它最早是由贝尔实验室的DennisRitchie为了UNIX的辅助开发而写的,开始时UNIX是用汇编语言和一种叫B的语言编写的。
从那时候起,C就成为世界上使用最广泛计算机语言。
C能在编程领域里得到如此广泛支持的原因有以下一些:
·它是一种非常通用的语言.几乎你所能想到的任何一种计算机上都有至少一种能用的C编译器。
并且它的语法和函数库在不同的平台上都是统一的,这个特性对开发者来说很有吸引力。
·用C写的程序执行速度很快。
·C是所有版本的UNIX上的系统语言。
C在过去的二十年中有了很大的发展。
在80年代末期美国国家标准协会(AmericanNationalStandardsInstitute)发布了一个被称为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的指南页,在命令行上键入mangcc。
调试和剖析选项
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%sn”,string);
}
voidmy_print2(char*string)
{
char*string2;
intsize,i;
size=strlen(string);
string2=(char*)malloc(size+1);
for(i=0;i〈size;i++)
string2[size-i]=string[i];
string2[size+1]=`%content%’;
printf("Thestringprintedbackwardis%sn”,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%sn”,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]=`%content%';
26printf(”Thestringprintedbackwardis%sn”,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`%content%00’
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%sn”,string);
}
my_print2(char*string)
{
char*string2;
intsize,size2,i;
size=strlen(string);
size2=size-1;
string2=(char*)malloc(size+1);
for(i=0;i〈size;i++)
string2[size2—i]=string[i];
string2[size]=`%content%';
printf(