新手上路 C语言教程_第1页
新手上路 C语言教程_第2页
新手上路 C语言教程_第3页
新手上路 C语言教程_第4页
新手上路 C语言教程_第5页
已阅读5页,还剩73页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、本文包括:1. 1. 引言 2. 2. 什么是C语言? 3. 3. 最简单的C程序 4. 4. C程序编译过程解析 5. 5. 变量:程序保存结果的手段 6. 6. Printf 7. 7. Printf:读取用户输入 8. 8. Scanf 9. 9. if语句和while循环 10. 10. 示例:编写能够打印华氏-摄氏温度转换表的程序 11. 11. 数组12. 12. 

2、更多变量知识 13. 13. 函数 14. 14. 函数原型 15. 15. 函数库 16. 16. 创建一个函数库17. 17. makefile 18. 18. 文本文件 19. 19. 打开文本文件 20. 20. 读取文本文件 21. 21. 指针22. 22. 为什么要用指针? 23. 23. 指针基础 24. 24. 内存地址 25. 25. 指

3、向同一地址 26. 26. 指针常见错误 27. 27. 指针作为函数参数 28. 28. 动态数据结构 29. 29. 计算机中的栈和堆30. 30. malloc函数和free函数 31. 31. 指针进阶 32. 32. 指向结构体的指针 33. 33. 指向指针的指针 34. 34. 链表栈 35. 35. 用指针操作数组 36. 36. 字符串 37. 37.

4、0;字符串须知 38. 38. 运算符优先级 39. 39. 命令行参数 40. 40. 二进制文件 41. 41. 了解更多信息 42. 42. 阅读所有软件类文章C语言是一种广受欢迎且用途广泛的程序设计语言。人们使用它编写计算机程序。因为C功能强、效率高,所以全世界的程序员都喜欢用它。如果您是一名程序员,或者希望成为一名程序员的话,那么学习C语言会使您受益匪浅:C语言入门教程· 您将可以在许多平台上阅读和编写程序。小到微控制器,大到最先进的科研系统,到处都是C语言的用武之地。许多现

5、代操作系统也是用C语言编写的。· 学习过C语言后,再进阶的面向对象C+语言就容易多了。C+是对C的扩展。不先掌握C语言而直接学习C+是非常困难的。本文中,我们将会循序渐进地完整介绍C语言,并教会您怎样从零起步成为一名C程序员。一旦您掌握了C语言,您一定会对自己可以做的事情感到吃惊的!C是一种计算机编程语言。就是说可以用C编写指令清单交给计算机去执行。C是当前正在使用的成千上万种编程语言之一。它已经有几十年的历史了。因为它功能强大而且效率很高,所以受到了程序员们广泛的欢迎。C还是一种容易学习的语言。和其他一些语言相比,C的风格稍有点古怪,但您会很快适应的。HSWC语言编译器C 是所谓的

6、编译型语言,意思是说您写好的源程序必须通过C编译器来运行,使它变成可以在计算机上运行(执行)的可执行文件。C源程序是供人阅读的,而编译器产生的可执行程序是供计算机读入和执行的。也就是说,要编写并运行一个C程序,您必须借助于C编译器。如果使用UNIX系统(例如,您在UNIX主机上用C编写CGI脚本,或者您是学生,在实验室的UNIX机器上工作),C编译器是免费提供的。它的名字是cc或者gcc,可以通过命令行调用。您如果是学生,那么学校很可能会向您提供编译器。您只要查一下学校提供什么样的编译器并学习使用即可。如果您是在家使用Windows操作系统的计算机,则需要下载免费的C编译器或是购买商业的C编译

7、器。微软公司的Visual C+是一套广泛使用的商业编译环境,可以编译C和C+程序。可惜这套软件要花费几百美元。如果不想花那么多钱购买商业编译器的话,那么您可以在网上挑选一款免费的编译器。如果您想找的话,不妨从我们会从一个极其简单的C语言程序为例开始,循序渐进。在这个例子中,我假定您使用的是UNIX命令行,并以gcc作为您的开发环境。如果您使用的并不是这些,所有的代码依然可以正常运行您只是需要使用其他可用的编译器,才能理解这些代码。让我们从一个最简单的C程序开始,了解一下C的基本要素和编译过程。将下面的程序录入一个标准的文本编辑器(UNIX上用vi或emacs、Windows上用Notepad

8、、Macintosh上用TeachText),然后以samp.c的文件名保存。文件名如果没有.c结尾,编译的时候就很可能会出现一些错误,所以要确保文件名以.c结尾。另外,请确保您的编辑器不会自动在文件名末尾添加后缀(如.txt)。下面是我们的第一个程序:#include int main()printf(" 这是我第一个程序的输出!n");return 0;执行时,程序使计算机输出“这是我第一个程序的输出!”,之后便退出了。真是简单到不能再简单了!书写位置录入本程序时,注意将#include中的“#”放在第一列,即最左侧。其他行的间距和缩进可随个人喜好选择。某些U

9、NIX系统上有一个叫cb的C程序美化器,它可以为您美化代码格式。上例程序代码的空格和缩进风格可作为格式的范例。请按下面步骤编译本程序:· 在 UNIX 系统上,键入gcc samp.c -o samp(若gcc不能用就用cc)。此命令行的作用是调用名为gcc的C编译器来编译samp.c,并将生成的可执行文件命名为samp。要运行编译好的程序,请键入samp(有些 UNIX 系统上要键入./samp)。· 在使用在使用DJGPP的DOS和Windows电脑上,请在MS-DOS命令中键入gcc samp.c -o samp.exe。这条命令会调用名叫gcc的C编译器,使它编译s

10、amp.c,并生成一个可执行文件samp.exe。要运行程序,只需键入samp。· 如果您使用的是其他编译器或其他开发系统,阅读并遵循该编译器的指南。当您运行这个程序后,您应该会看到“这是我第一个程序的输出!”的打印了。当您编译这个程序时,到底发生了什么?下面是示意图:HSWC语言编译器如果您键入程序时打错了,它要么不会编译,要么不会运行。如果程序没有编译,或者没有正确地运行,请重新编辑,检查输入的时候是否有误。然后修正错误,重新编译。让我们逐行讲解一下这个示例程序,看看每一行都做了什么(点击这里在新窗口中打开程序):· 程序以#include 开始。这一行将“标

11、准输入输出库(Standard I/O library)”包括进程序之中。使用标准输入输出库您可以完成诸如从键盘(称为“标准输入”)读入数据、向屏幕(称为“标准输出”)写出数据、处理磁盘上的文本文件之类的工作。这是一个极有用的函数库。C拥有大量像stdio一样的函数库,包括字符串、时间和数学函数库等。函数库就是一个别人写好供我们调用的代码包,它使我们编程变得更加容易(我们在后面讨论函数库)。· int main()这行声明了主函数。所有的C程序都必须包含一个名为main的函数。我们稍后将学习更多关于函数的知识。程序从主函数的第一行开始运行。· C语言使用花括号( 

12、和 )标识一个代码块的开始和结束。本例中构成主函数的代码块包括两行。· printf语句将输出的内容送往标准输出设备(对我们而言就是屏幕)。引号中的部分叫做格式字符串,用于表示数据输出时的格式。格式字符串可以包含文字(“这是我第一个程序的输出!”)、回车换行符(n)和标识变量位置的操作符等。如果您使用的是UNIX系统,可以键入man 3 printf获得printf函数的完整说明文档。其他系统下您可以通过阅读编译器相关的文档来了解printf函数的细节。· return 0;这行使主函数向运行它的命令行解释器返回错误代码0(表示没有错误)。稍后我们还会继续介绍这一

13、特性。作为程序员,您的程序常常需要“记住”某个数值。比如您的程序要求用户输入一个数,或者它计算出了一个结果,您需要把它保存在某处供以后使用。程序是靠使用变量来保存结果的。例如:int b;上面这行的意思是说:“我想创建一个可以容纳一个整数的存储空间,名字叫做b。”每个变量都有名字(本例中是b)和类型(本例中是int,表示整型)。您可以用如下语句在b中存储一个值:b=5;您可以用如下语句使用 b 中保存的值:printf("%d", b);C 语言提供了几种标准的变量类型:· int - 整型,用于保存整数值· float - 用于保存

14、浮点值· char - 用于保存单个字符值,如“m”或“Z”后面我们会看到整型以外其他类型的例子。 printf语句将要输出的内容送往标准输出设备。对我们而言,标准输出设备一般就是计算机屏幕(虽然您也可以将标准输出重定向到一个文本文件或另一个命令)。下面这个程序将有助于您深入理解printf:#include int main()int a, b, c;a=5;b=7;c=a+b;printf("%d+%d=%dn", a, b, c);return 0;请录入程序并保存,文件名是add.c。在命令行键入gcc add.c -o ad

15、d进行编译,之后再键入add(或./add)运行程序。您会看到输出:“5+7=12”。下面是该程序各行的解释:· int a, b, c; 这行声明了三个分别名为a、b和c的整型变量。整型变量用于保存整数。· 下一行将变量a的值初始化为5。· 再下一行将变量b的值初始化为7。· 再下一行将a和b相加,并将结果“赋值”给c。计算机将a中的值(5) 和b中的值(7) 相加得到结果12,并把新值(12) 存入变量c。变量c被赋值12。因此,本行中的“=”称为“赋值运算符”。· 接下来printf语句会打印“5+7=12”。占位符%d的作用是

16、为输出值占位。本行有三个%d占位符,末尾有三个变量名:a、b和c。C语言将第一个%d和a匹配并替换为5;将第二个%d和b匹配并替换为7;将第三个%d和c匹配并替换为12。然后在屏幕上打印替换结果:“5+7=12”。+、=及空格作为格式字符串的一部分,按程序员指定的顺序自动插入到%d运算符之间。前面的程序很好,但要是如果能从用户那里读入值5和7而不是使用固定的值的话,那就更好了。可以改为下面的程序:#include int main() int a, b, c; printf("输入第一个值:"); scanf("%d", printf("输入第

17、二个值:"); scanf("%d", c = a + b; printf("%d + %d = %dn", a, b, c); return 0;以下是程序的执行过程:HSWC语言执行程序示例请按照上例修改原来的程序,然后重新编译和执行并确保它能正常工作。注意scanf和printf都要使用格式字符串(可以键入man scanf获取更多信息)。还要注意a和b前面都有一个“&”。这是C语言中的取地址运算符,它会返回变数的地址(在我们讨论指针之前,这个符号没什么用)。在对单个字符,整数,或者浮点数形式的任何变量做标准输入,以及对结构形式(

18、我们很快就会接触到)的任何变量做标准输入时,您都必须使用&运算符。如果您遗漏了&运算符,您在运行程序时会报错。您可以试试,这样就能看到这种运行错误是什么样子。让我们通过一些变量来完全理解printf。这是最简单的printf声明:printf("您好");它的格式字符串告诉printf把“您好”二字发送到标准输出。对比:printf("您好n");两者的区别是后者在“您好”后跟一个回车换行,一起送到标准输出。下面这行显示了如何使用printf输出一个变量的值。printf("%d", b)%d是占位符,会在printf

19、语句执行时替换为变量b的值。通常您需要把输出值嵌入到一些文字之中。一种方法是这样:printf("温度是");printf("%d", b);printf(" 度n");更简单的方法是这样写:printf("温度是 %d 度n", b);一个printf语句中可以使用多个%d占位符:printf("%d + %d = %dn", a, b, c);在printf语句中,格式字符串包含的操作符数目和后面的变量的数目和类型一定要一致,这一点极为重要。例如,如果格式字符串包含有三个%d操作符,那么格式

20、字符串后面必须紧接正好三个参数,且参数的类型和先后顺序必须和操作符指定的是一样的。使用不同形式的占位符,printf可以打印所有的C标准类型:· int(整数)用%d· float(浮点数)用%f· char(单个字符)用%c· 字符串(字符数组,见后面讨论)用%s在UNIX机器上要了解printf的更多细节,可以键入man 3 printf。如果使用任何其他C编译器,则您很可能在其附带手册或帮助文件中找到对printf的说明。 使用scanf函数可以从标准输入设备接受输入。对我们而言,标准输入设备一般就是键盘。虽然scanf函数可以做许多事情

21、,但是除了最简单的工作外,一般而言并不很可靠。原因是它不能很好地处理人为错误。不过,对于简单的程序来说,scanf还是很合适的,而且易于使用。scanf的最简单用法是像这样: scanf("%d", &b);该程序将读取用户从键盘输入的一个整数(和在printf中一样,%d代表整型,因此b必须声明为int),并将其存入b。scanf函数使用的占位符和printf的相同:· int用%d· float用%f· char用%c· 字符串(后面会讨论)用%s在使用scanf时,您必须在其变量前加上&。为什么要这么做的原因,在

22、您学习指针后会很清楚。&符号很容易忽略,而一旦您忘记添加&,几乎每次运行程序都会崩溃。通常来说,最好像在这里举的例子一样使用scanf函数读取键盘输入的单个数值。读取多个数值的时候,请多次调用scanf函数。不过,在任何一个实际的(而不是用来演示的)程序中,您将不会使用scan函数,而是使用gets或者fgets函数来一次一整行地阅读文本。然后您会分析这一行,并读取它的值。这么做的原因是,您可以检测到输入的错误,并做恰当的处理。要完全理解printf和scanf函数,需要一些练习。一旦掌握了,它们非常有用。试试这个!· 修改这个程序,使它能接受3个数值,而不是2个,并

23、把3个数值相加。#include int main() int a, b, c; printf("输入第一个值:"); scanf("%d", &a); printf("输入第二个值:"); scanf("%d", &b); c = a + b; printf("%d + %d = %dn", a, b, c); return 0;· 试着在上述程序中随意删除或添加字符或者词语,看编译器对这些错误如何反应。比如说,在上述程序的第一行删除b变量,看看当您忘记了声明变量时,

24、编译器会怎么办。删除一个分号,看会发生什么。或者漏掉一半大括号。删掉主函数旁边的一个圆括号。尝试每一种错误,然后通过编译器运行程序,看会发生什么。通过模拟这些错误,您会了解不同种类的编译器错误,并在当您真正地犯了这些错误的时候,更容易找到这些输入错误。C常见错误· 用错大小写。C语言区分大小写,因此Printf和PRINTF都不对,必须是printf。· 在scanf中忘了使用&。· 在printf或scanf的格式声明中参数太多,或者太少。· 在使用变量的时候没有事先声明。C语言中,if语句和while循环都会用到布尔表达式。下面是一个使用if

25、语句的简单例子:#include int main()int b;printf("输入一个值:");scanf("%d", &b);if (b < 0)printf("值为负数n");return 0;程序从用户读入一个数字,然后用if语句测试这个数字是否小于零。如果小于零则打印一条消息,否则什么也不做。程序中的(b<0)称为布尔表达式。C需要求出此表达式的值才能决定是否打印后面的消息。如果布尔表达式的结果是真,那么C将执行紧接在if语句后面的一行代码(或花括号中的代码块);如果布尔表达式的结果是假,那么

26、C将跳过紧接在if语句后面的代码行或代码块。下面是一个稍复杂一点的例子:#includeint main()int b;printf("输入一个值:");scanf("%d", &b);if (b < 0)printf("值为负数n");else if (b = 0)printf("值为0n");elseprintf("值为正数n");return 0;这个例子中的else if和else部分处理了值为零或为正的情况。下面是一个更复杂一些的布尔表达式:if (x=y) &&

27、amp; (j>k)z=1;elseq=10;上面这个if语句的意思是:“如果变量x和y的值相等,而且变量j的值大于k的值,那么令变量z的值等于1,否则令变量q的值等于10。”在C程序中,您经常使用类似这样的if语句进行判断。一般而言,大多数判断都很简单,像第一个例子。但偶尔也会遇到复杂一些的情况。请注意,C使用=判断相等,而用=为变量赋值。&&在C中代表布尔操作符与。这是C语言中的所有的布尔操作符:等于 =小于 <大于 >小于等于 <=大于等于 >=不等于 !=与 &&或 |非 !您会发现,while声明和if声明使用起来一样简单

28、。举个例子:while (a < b)printf("%dn", a);a = a + 1;这会反复运行大括号中间的两行语句,直到a大于或等于b。大体来说,while声明是这样工作的:C还提供一种do-while结构:doprintf("%dn", a);a = a + 1;while (a < b);C中的for循环只不过是while语句的精简写法。例如,您有如下C代码:x=1;while (x<10)x+; 您可以用for循环改写成下面这样:for(x=1; x<10; x+)请注意,while循环实际包括了三个步骤

29、:初始化(x=1)、判断(x<10)和增量(x+)。使用for循环可以把三个步骤写在同一行中,但并不限制三个部分的具体内容。例如,您有如下C代码:a=1;b=6;while (a < b)a+;printf("%dn",a);用for循环就可以写为:for (a=1,b=6; a < b; a+,printf("%dn",a);这样虽然有点不易读懂,但还是允许的。在for循环的初始化和增量(不包括测试)部分,我们可以使用多条语句并用逗号操作符加以分隔。许多 C 程序员喜欢用一行代码“浓缩”进很多信息。但也有许多人认为这样会使代码更加难以

30、理解,所以他们选择分开来写。布尔表达式中=和=的对比=符号是C语言的一个常见的“陷阱”,因为时不时会由于疏忽将其写成=。这个错误很容易犯,但对于编译器来说两者的含义截然不同。C的布尔表达式既接受=也接受=,然而程序的执行却大相径庭。C的布尔表达式求值的结果是整数,所以整数也可以直接作为布尔表达式使用。C中整数零代表假,其他任何非零整数代表真。下面是的C代码是合法的:#include int main() int a; printf("输入一个数字:"); scanf("%d", &a); if (a) printf("值为真n"

31、;); return 0;如果a是任何非零值,printf语句就会执行。在C中,诸如if (a=b)这样的语句的意义是:“将b赋值给a,然后判断a的布尔值。”如果a变成0,则if语句判断为假,否则为真。也就是说a的值在判断过程中改变了。如果您本该写=的,那么以上行为显然不是您的本意(尽管正确使用时还可利用这一特性)。所以在使用=和=的时候请格外小心。假设您要编写一个能够打印华氏-摄氏温度转换表的程序。可以使用for或者while循环可以轻松实现:#include int main()int a;a=0;while (a <=100)printf("%4d华氏度=%4d

32、摄氏度n",a,(a -32.0)*5.0/9.0);a =a+10;return 0;该程序运行后将打印从华氏0度到华氏100度的转换表。输出如下:0华氏度=-17摄氏度10华氏度=-12摄氏度20华氏度=-6摄氏度30华氏度=-1摄氏度40华氏度=4摄氏度50华氏度=10摄氏度60华氏度=15摄氏度70华氏度=21摄氏度80华氏度=26摄氏度90华氏度=32摄氏度100华氏度=37摄氏度上表中温度值以10度为间隔递增。您会发现通过程序改变表中温度的起始值、结束值和递增值都很容易。如果想让温度值更加精确的话,您可以改用浮点数值:#include int main()flo

33、at a;a = 0;while (a <= 100)printf("%6.2f 华氏度 = %6.2f 摄氏度n",a, (a - 32.0) * 5.0 / 9.0);a = a + 10;return 0;如上例所示,a的声明改用float,而且在printf语句中用符号%f代替了符号%d。此外,还在符号%f前规定了一些格式:打印数值时使用6位整数和2位小数。现在我们要修改程序,使它在适当的位置插入98.6度。即,我们还是每隔10度打印,但是要增加一行98.6华氏度,因为它是人体的正常体温。下面的程序会满足我们的要求:#include int main

34、()float a;a = 0;while (a <= 100)if (a > 98.6)printf("%6.2f 华氏度 = %6.2f 摄氏度n",98.6, (98.6 - 32.0) * 5.0 / 9.0);printf("%6.2f 华氏度 = %6.2f 摄氏度n",a, (a - 32.0) * 5.0 / 9.0);a = a + 10;return 0;结束值是100时,上面的程序可以正常工作。但如果将结束值变成200,您就会发现程序的一处错误:打印了太多次98.6度。我们可以用多种方法改正这个错误。下面是其中之一:#i

35、ncludeint main()float a, b;a = 0;b = -1;while (a <= 100)if (a > 98.6) && (b < 98.6)printf("%6.2f 华氏度 = %6.2f 摄氏度n",98.6, (98.6 - 32.0) * 5.0 / 9.0);printf("%6.2f 华氏度 = %6.2f 摄氏度n",a, (a - 32.0) * 5.0 / 9.0);b = a;a = a + 10;return 0;动手一试· 尝试修改华氏-摄氏度转换程序,以使该程

36、序使用scanf接受用户输入,作为结果输出表格的起始、结束和增量值。· 为表格制作表头。· 试用另一种方法解决上例的程序改正的错误。· 制作一个英镑-千克或英里-公里的转换表。C 常见错误· 在if或while语句中错用=代替=。· 忘记在while循环中增加计数器的值这会导致无限循环(永不结束的循环)。· 不留神在for循环或if语句末尾加上了分号,使语句起不了作用。例如:for (x=1; x<10; x+);printf("%dn",x);只打印出一个值,因为for语句循环执行的只是它后面的那个分号。&

37、#160;本节我们将用C写一个小程序随机生成10个数,并对其进行排序。我们将使用一种新的变量结构:数组。借助数组可以声明并使用一组同类型的数据。比如您现在需要创建五个整数。一种办法是直接进行声明:int a, b, c, d, e;这当然可以,但如果您需要一千个整数呢?一种更好的办法是声明一个包含五个整数的数组:int a5;这个数组中的五个整数可以分别使用下标进行访问。C中所有数组的下标都是从零到n-1。所以int a5;包含了五个元素。例如:int a5;a0 = 12;a1 = 9;a2 = 14;a3 = 5;a4 = 1;使用下标访问数组带来的一个好处是可以用循环来遍历下标。例如,下

38、面的代码把数组的所有元素初始化为零:int a5;int i;for (i=0; i<5; i+)ai = 0;下面的代码将数组元素按顺序初始化,然后打印输出:#include int main()int a5;int i;for (i=0; i<5; i+)ai = i;for (i=0; i<5; i+)printf("a%d = %dn", i, ai);在C中到处都会用到数组。下面的代码是一种使用数组的常见情形。请打开编辑器并录入以下代码:#include #define MAX 10int aMAX;int rand_seed=10;/

39、* 摘自 K&R - 返回0-32767之间一个随机数字。*/int rand() rand_seed = rand_seed * 1103515245 +12345; return (unsigned int)(rand_seed / 65536) % 32768;int main() int i,t,x,y; /* fill array */ for (i=0; i < MAX; i+) ai=rand(); printf("%dn",ai); /* 这里很快会加入新内容 */return 0;这段代码引入了几个新概念。#define声明了一个名为MAX的

40、常量并让它等于10。为在代码中醒目起见,常量名习惯上全用大写字母。int aMAX;这行告诉我们在C中如何声明整型数组。注意,数组声明出现的位置决定了它是整个程序的全局变量。下面的一行int rand_seed=10也声明了一个全局变量,名为rand_seed。它在每次程序开始时被初始化为10,作为下面随机数生成代码的初始种子。实际中随机数发生器的种子应该以一个随机数作为初值,如系统时间。本例中的rand函数,在每次程序运行时都会给出相同的随机数序列。int rand()这行代码是一个函数声明:rand函数没有参数且返回一个整数。后面我们会学习更多关于函数的知识。下面的四行是rand函数的实现

41、,我们可以暂时忽略。主函数没什么特别的。先声明了四个整型变量,接下来的for循环用10个随机数填充数组。注意数组a包含了10个独立的整数。您可以用方括号指明具体元素。例如a0代表数组中的第一个整数,a1代表第二个,以此类推。用/*开始并用*/结束的行称为注释。编译器完全忽略注释行。您可以把程序说明(自己看或供其他程序员阅读)放在注释中。现在请用下面的代码替换这里很快会加入新内容那行注释:/* 将数组用冒泡法排序 */for (x=0; x < MAX-1; x+) for (y=0; y < MAX-x-1; y+) if (ay > ay+1) t=ay; ay=ay+1;

42、 ay+1=t; /* 打印排序后的数组 */printf("-n");for (i=0; i < MAX; i+)printf("%dn",ai);以上代码将随机数排序,并按顺序打印。每次运行程序您都会得到同样的结果。要想改变参加排序的值,在每次运行程序前请改变rand_seed的值。要真正读懂这段代码的简单办法只有一个,就是“手动”执行一下程序。为简单起见令MAX等于4。准备一张纸并假设您自己就是计算机。在纸上画出数组,然后填入四个随机、无序的数字。现在按照代码中的排序部分一边逐行执行,一边在纸上记录执行过程。您会发现每次执行完内层循环后,较大

43、的那些数被推向数组尾部,而较小的那些数则向数组首部靠拢。动手一试· 在代码前一部分,试将填充数组的for循环缩减为一行代码。请确保结果和原来的程序相同。· 将冒泡排序的代码提出来建立它自己的函数。函数头是void bubble_sort()。然后将冒泡排序使用的变量也移入函数变成局部变量。因为数组是全局的,所以不需要传递参数。· 改变随机数种子的初始值。C 常见错误· C不提供边界检查。所以如果您的数组指标越界了,C不会有任何提示。程序会最后崩溃或输出无意义的数据。· 即使不传递参数,函数调用也要包含()。例如,C接受x=rand;,但这不能完

44、成函数调用,而是把rand函数的内存地址赋值给了x。必须写成x=rand();才行。变量类型C提供三种标准变量类型:· 整数类型:int· 浮点数类型:float· 字符类型:charint表示整数,占4个字节;float表示浮点数,也占4个字节;char表示单个字符(如“a”或“3”),占1个字节;字符串被声明为字符数组。C还提供如下派生类型:· double(8字节浮点数类型)· short(2字节整数类型)· unsigned short或unsigned int(非负整数,没有符号位)运算符及其优先级C中的运算符和大多数语言类

45、似:+ 加- 减/ 除* 乘% 取模/ 运算符在操作数都是整数的情况下执行整数除法运算,否则执行浮点数除法运算。例如:void main()float a;a=10/3;printf("%fn",a);上面这段代码将会打印出一个浮点数,因为a被声明为float。但由于执行的是整数除法运算,所以a的值是3.0。C的运算符优先级和大多数语言的也很类似,即先乘除,后加减。5+3*4 的结果是17,不是32,因为*比+有更高的优先级。使用括号可以改变正常的优先级顺序:(5+3)*4的结果是32。因为在括号中5+3优先计算。我们后面还将遇到运算符优先级引入指针后C的运算符优

46、先级会变得复杂一些。类型转换C可以自动类型转换。特别是使用指针时需要经常这样做。某些类型间的赋值操作时也会发生类型转换。例如,上面的程序中整型数值被自动转换为浮点型。显式类型转换时,将目的类型放在括号中,并放在要转换的数据之前。例如,在上面的程序中,将a=10/3;替换为a=(float)10/3;后会得到结果3.33333,因为10在做除法之前已被转换为浮点数了。Typedef使用C的typedef语句,用户可以声明自定义的类型。例如,下面代码定义了一个C程序中经常用到的类型:#define TRUE 1#define FALSE 0typedef int boolean;void main

47、()boolean b;b=FALSE;这段代码在C程序中声明布尔型的变量。如果您不喜欢把实数的类型称为“float”,您可以这样写: typedef float real;然后就可以使用实数类型了: real r1,r2,r3;只要在使用之前定义,typedef 语句可以出现在C程序的任何地方。结构体在 C 语言中可以使用结构体把变量组织到一起。例如:struct recint a,b,c;float d,e,f;struct rec r;如上例所示,每当要声明rec型的结构体时,都要写struct rec。这条规定很容易被忘记。一旦您由于疏忽忘了写上struct,就会遇

48、到很多编译错误。上面的代码可以缩减为下面的形式:struct recint a,b,c;float d,e,f; r;即用一个语句同时声明了rec类型和一个rec类型的变量r。您也可以使用typedef 语句定义结构体类型的名字。例如,如果您不喜欢每次声明一个结构体变量时都去写struct rec r,则您可以用:typedef struct rec rec_type;定义一个新类型,然后就可以声明rec_type类型的变量了:rec_type r;使用句点(.)访问结构体的成员。如r.a=5;。数组在一般变量声明的变量名后加上数组的大小即可声明一个数组,如下所示:int a10; /* 整数

49、数组 */char s100; /* 字符数组(C 的字符串) */float f20; /* 实数数组 */struct rec r50; /* 结构体数组 */增量长记法 短记法i=i+1; i+;i=i-1; i-;i=i+3; i += 3;i=i*j; i *= j;动手一试· 试写几段代码研究一下类型转换和运算符优先级。考查对象包括int、char、float等。· 创建一个结构体数组,然后编写一段代码,使其基于结构体的一个整型成员对结构体数组排序。C 常见错误· 如前所述,当使用/运算符计算两个整数时常常会得出意想不到的结果。用前请三思。大多数编程语

50、言都支持定义某种形式的函数。使用函数可以将一个大程序分解为小的代码块并分别命名,然后这些命名的代码块就可以在整个程序中重复使用了。函数输入参数并返回一个结果。C函数可以输入的参数个数没有限制。一般来说,对于编译器而言,只要在调用函数以前事先声明函数名称,则各函数在程序中的顺序并不重要。我们已经讨论过一些有关函数的内容了。前面遇到的rand函数几乎是最简单的函数了。它不接受参数并返回一个整数:int rand()/* 摘自 K&R - 生成一个0-32767之间的随机数字。*/ rand_seed = rand_seed * 1103515245 +12345; return (unsi

51、gned int)(rand_seed / 65536) % 32768;int rand()这些程序定义了rand函数,还指明rand没有输入参数并返回一个整数。此函数没有局部变量,但如果需要的话,局部变量会紧接着打开的开始。(C 允许您在任何  后面声明变量,这些变量一直存在,直到程序执行至那个匹配的 为止,此后即不复存在。因此函数的局部变量在遇到函数中匹配的 时就消失了。在其生存周期内,局部变量被保存在系统栈上。)请注意第一行的()后面没有;。您若不小心加上了分号,编译器会给出一大堆莫名其妙的错误信息。另外还要注意即使当函数没有参数时也要使用()。括号告诉编译器您在声

52、明一个函数而不是简单地声明一个int类型。对于任何要返回结果的函数来说,return语句是不可或缺的。它指明函数要返回的值并使函数立即退出。因此您可以在函数中放置多个return语句作为函数的多个退出点。若函数中没有return语句,则函数在执行到 时退出并返回一个随机值(这时很多编译器会发出警告)。C中的函数可以返回任何类型的值:包括int、float、char、结构体等等。调用rand函数的方式有许多种。例如:x=rand();。此语句中x被赋值为rand的返回值。注意,在函数调用中即使没有参数也必须使用(),否则x将被赋值为rand函数的内存地址,而这通常并非您的本意。也可以用这种方式调

53、用rand:if (rand() > 100)或:rand();后一种方式中,rand函数被调用了,可是其返回值随即被丢弃。您可能不会以这种方式使用rand,但很多函数会返回某种错误代码作为调用结果,而您又不关心这些错误代码的话(如您知道不可能发生错误),就可以用这种调用方式将其丢弃。如果您不想返回任何值的话,函数可以使用void作为返回类型。例如:void print_header() printf("第一个程序n"); printf("作者 Marshall Brainn"); printf("版本 1.0, 发布于1991年12月2

54、6日n");此函数没有返回值。可以像下面这样调用:print_header();调用时必须加上(),否则函数不会被调用,即使此语句在很多系统上能通过编译。C函数可以接受任何类型的参数。例如: fact(int i) int j,k; j=1; for (k=2; k<=i; k+) j=j*k; return j;返回传入的整型参数i的阶乘。用逗号分隔多个参数: int add (int i, int j) return i+j;C语言经历了许多年的演变。有时您会看到用“旧式风格”编写的函数。以add为例:int add(i,j)int i;int j;return

55、i+j;您应该能够读懂旧式代码。它们在执行上并无不同,只不过是另一种书写格式罢了。编写程序时应该使用“新式风格”(称为ANSI C),把类型声明写在参数列表中;除非您要把代码交付给只有“旧式”(非ANSI C)编译器的人。现在,把程序中的所有函数声明函数原型已成为公认的良好编程风格。函数原型在真正定义函数之前,向后面的代码声明了函数的名称、参数和返回值的类型。为了理解函数原型的用处,请录入下面的程序并运行:#include void main() printf("%dn",add(3);int add(int i, int j) return i+j;尽管add要求传入两个

56、参数而实际只传入了一个,这段代码还是能在很多编译器上通过编译而不会产成警告。这是因为很多C编译器既不对参数类型也不对参数个数做匹配性检查。于是您可能仅是因为不小心多传或少传了一个参数而要浪费大量时间调试代码。上面的代码能顺利通过编译,却给出错误的结果。针对此问题,C语言允许您在程序开始处(实际是任何地方)声明函数原型。这样一来,C就会检查所有参数列表的类型和数目。试试编译下面的程序:#include int add (int,int); /* add 的函数原型 */void main() printf("%dn",add(3);int add(int i, int j)

57、return i+j;函数原型使编译器对printf语句报错。请在程序开始处为每个函数声明原型。这样可以为您节省大量的调试时间,还顺便解决了不能在定义前使用函数的问题。例如,下面的代码不能通过编译:#include void main() printf("%dn",add(3);float add(int i, int j) return i+j;您也许会问:为什么add返回int时可以编译,返回float时就不行了?因为老一些的C编译器默认函数返回一个int值。使用函数原型可以解决这个问题。“旧式”(非ANSI)编译器也支持函数原型,但原型中的参数列表必须为空。旧式编译器不对参数列表进行错误检查。动手一试· 请返回到前面介绍的那个冒泡法排序的例子,为冒泡排序创建一个函数

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论