C语言程序设计讲座

Author: 南湘 Date: 1994-01-28

        
        该课程是关于程序设计的一门重要课程,通过对该课程的学习,学生应能掌握C语言的语法结构,一般C程序的编写、调试和上机运行,并能解决一定的实际问题。
        C语言程序设计包括两个方面的内容:C语言的主要语法规则和C语言的程序设计,程序设计贯穿始终,不要求学生死记硬背语法规定,而应当通过多看程序,多编程序,多上机调试程序来熟悉C语言语法规则的使用。
        要特别注意算法的设计,即学会分析问题,整理出思路,画出流程图,要求掌握结构化程序设计方法,养成程序设计的良好习惯和风格。程序设计应当是深思熟虑的结果,而不是信手拈来的产物。
        这里的辅导以谭浩强编写的《C语言程序设计》(清华大学出版社出版)为教材。学习本课程最好具有一定的英语基础,以便对C语言的各种提示有一个较全面的认识。
        在教材的第一章,是C语言概述,对C语言出现的历史背景及C语言的特点作了介绍,在此可认识一下C语言的程序结构。
        本章没有什么难点,对于C语言的特点,在学好C语言之后就会自然明白。
        
        第二章   数据类型  运算符与表达式
        
        要求知道C语言有一些什么数据类型,并对基本的数据类型有一个较全面的了解,常量与变量有什么区别,变量名取法有什么规则。怎样定义变量的类型,几种基本数据类型的表现形式及在内存中存储形式有什么不同,不同数值型数据怎样进行混合运算。
        对于运算符,要求能知道C语言有哪几类运算符,每一种运算符怎样表示,表示形式是否和数学上的表现形式一致,表达式为本章的重点部分,要求能知道表达式有哪几种,掌握每一种表达式的正确书写以及表达式怎样求值。
        难点是表达式的求值问题和表达式的正确书写。但是在本章中只要求能掌握算术表达式,赋值表达式和逗号表达式的正确书写和求值即可。
        下面对难点和重点作一些必要说明:
        例如:将下列式子写成C语言的表达式
        ab+cd
        (1) ____
        efgh
        ________
        (2) y=(-b+∨b×b-4ac)/2a
        其中a,b,c,d,e,f,g,h,都为变量
        (1)为:(a*b+c*d)/(e*f*g*h)
        式中两个括号不能省去,若省去,将会得到其它式子。
        去掉分子的括号:a*b+c*d/(e*f*g*h)这时含义为:
        d
        ab+c×__
        efgh
        去掉分母的括号:(a*b+c*d)/e*f*g*h这时含义为:
        ab+cd
        ___×fgh
        e
        两个括号都去掉:a*b+c*d/e*f*g*h这时含义为:
        d
        ab+c×__×fgh
        e
        因此,要想写出正确的C语言表达式,首先必须要求表达式的含义与原含义相同,其次再考虑语句正确与否。
        (2)为:y=(-b+SQRT(b*b-4*a*c))/(2*a)
        在此表达式中,要求注意括号的匹配关系,与数学上不一样的是C语句中无中括号、小括号之分,一律用小括号代替。
        表达式求值举例如下:
        例:设a=1,b=4,c=2,d=3求a+b/c*d-c*a之值,即求表达式:a+b/c*d-c*a之值,规则为:先算高一级运算,同一级运算从左到右进行,上式值为5,但必须注意C语言中(后面将介绍)有的运算是从右到左的,与数学上不一致。
        另外,要想学好C语言,最好对数据在计算机内存的存放形式有一个全面的了解。并了解十进制数与二进制数怎样互相转化。
        
        第三章   最简单的C语言程序设计
        
        首先必须搞清楚什么是C语句,C语言中语句可以分几类,C语言中的语句与?其它高级语言有什么不同,搞清楚结构化程序设计的三种基本结构,并知道结?构化程序设计三种框图或流程图的画法,并能知道简单的库函数怎样调用,一些基本语句用法和各种类型数据的输入输出格式符和表示形式及其用法。本章?重点及难点是如何控制各种类型的数据的输入输出格式及函数怎样调用和流程?图的画法。
        现对本章的重点及难点作简要的说明:
        结构化程序设计的种流程图为:
        顺序流程图:
        A
        |
        B
        A和B者是看成语句,根据语句的书写顺序,先执行A,后执行B,最后执行?B后面的语句。
        判断选择流程图:
        条件
        成立|不成立
        ˉˉˉˉˉˉˉˉ
        A            B
        根据条件的成立与否来判断:是执行A还是执行B。与顺序结构不同的是,两者A和B中,只执行一个,不可能两个都执行。
        循环流程有当型循环和直到型循环两种。当型表示:
        当条件成立时,重复执行A。A可以是一条语句,也可以是一批语句。条件不?成立时,退出循环,执行A后面的语句。
        直到型:重复执行A,直到条件成立,退出循环,执行A的后面的语句。这时的?条件应为循环程序中的反条件,即程序中的条件为i<=100时,则流程图?中条件应写为i>100。
        关于库函数  的正确调用,首先必须搞清楚某一个库函数是包含在什么头文件?(即*.H)中,然后,在编程开始时用文件包含命令包含此头文件即可。
        例:若要求某一弧度的正弦函数值,首先必须知道此函数包含在什么头文件(?含在MATH.H)中,然后用包含文件命令#include“math.?h”放入程序的开始即可。接着在程序中调用SIN函数。
        最后是关于各种类型数据的输入/输出控制:
        首先必须搞清每种类型数据使用的格式字符,D用于十进制整数(有符号),?O用于无符号的八进制整数,X用于无符号的十六进制整数,U用于无符号的?十进制整数,C用于字符型数据,但只输出一个字符,S用于字符串,F、G?、E用于实型数据。其次要搞清楚是系统规定的宽度还是自己定义的宽度,当两者矛盾时,依据什么原则进行输入输出。
        先看输出形式:
        设i为整型数据,且i=345
        printf(“%d,%5d,%2d\”,i,i,i)
        则输出的结果应为:
        345  345345
        格式中%d按数的实际位数输出为3位,%5d按宽度为5位输出,但实际上?位数为3,帮左边补两个空格,%2d按宽度为2位输出,但与实际数有3位?发生矛盾,故系统隐含优先,仍以3位输出。对于O型格式符和X型格式符有?类似的情形,但输出的数不会出现负号,并且当数为负时,得到的为原数的补?码形式。
        现在考虑输入形式:
        输入方式基本上与输出类似,但有区别,现归纳如下:
        ■输入中对无符号整数不用%u而用%d,%o,%x;
        ■可用%md,%mo,%mx输入数据,但数据的值必须按规定的宽度?截取;
        ■输入中不能用%m.nf,%-m.nf,%m.ne,%-m.ne?;
        ■对于长整型的数可用%1d,%1o,%1x输入,短整型的数可以用?%hd,%ho,%hx输入;
        ■双实型的数据可以用%1f或%1e输入;
        其它规则等同于输出形式,不再讲述。
        
        第四章    逻辑运算与判断选择控制
        
        首先必须搞清楚逻辑运算符、关系运算符,关系表达式和逻辑表达式怎样书写,并能求出关系表达式和逻辑表达式的值,C语言中的关系值或逻辑值与其它语言不同,是用0和1来表示的,0表示假,或不成立,1表示真或成立。
        其次,搞清楚判断选择结构的程序怎样书写,以及怎样正确地使用判断选择结构。
        本章的重点及难点在于if语句的嵌套及switch语句的使用以及它们相互之间嵌套及嵌套中的从属关系怎样,都必须搞清楚。
        下面就本章的重点及难点作一些简要说明:
        if与else的配对关系,eles总是与它上面的最近的if匹配,若想改变它们的从属关系,可以使用复合语句中的花括号。
        例:
        if(条件1)
        if(条件2)
        语句1
        else
        语句2
        else
        if(条件3)
        语句3
        else
        语句4
        它们的从属关系为:
        条件1前的if与第二个else匹配,条件2前的if与第一个else匹配,条件3前的if与第三个else匹配
        if(条件1)
        (if(条件2)语句1)
        else
        if(条件3)语句2
        else语句3
        这时的从属关系为:条件1前的if与第一个else匹配,条件3前的if与第二个else匹配,条件2前的if没有与之匹配的else语句。
        从上面可以看出,语句中的书写顺序不变,加上若干个花括号就可以改变if与else的配对关系,因此,在编程时,千万要搞清楚它们之间的从属关系,否则,程序的含义将完全改变。
        if语句的嵌套中,很容易混淆各if语句与else的匹配关系,为了减少这种错误的发生,多路判断问题可以不用if语句的嵌套而改用switch语句实现,但并不是说if语句的嵌套就不能用,而是两者都可以用,并且它们之间可以互相转化。但是,每路判断结束时,要想从switch中
        退出时,必须有相应的break语句作为终止switch语句的标志。否则,将会一个分支接一个分支地执行下去,直到遇到switch的结束。
        例:switch(grade)
        {
        case "A":
        Printf("85-100\n");
        case "B":
        Printf("70-84\n");
        case "C":
        Printf("60-69\n");
        case "D":
        Printf("<60\n");
        default:
        Printf(errOr\n");
        }
        当grade的值为'A'时,输出结果为:
        85_100
        70_84
        60_69
        〈60
        error
        因此,上述得到的结果不符合我们的要求,为了得到唯一的结果,必须在每一个分支的后面加上break语句,现在将程序改动如下:
        Switch(grate)
        {
        case 'A':
        Printf(85-100\n");
        break;
        case 'B':
        Printf(70-84\n");
        break;
        case 'C':
        Printf(60-69\n");
        break;
        case 'D':
        Printf("<60\n");
        break;
        default:
        Printf("error\n");
        }
        对于多路判断问题,可参看第6项例4.7程序。
        
        第五章    循环控制
        
        了解循环结构的三种形式及每一种形式的表示形式和怎样使用循环,以及三种形式之间的转换。能较好地缩写循环程序并画出它的结构化流程图。
        本章的重点是搞清楚循环中的语句写法,是一条语句还是若干条语句,若是若干条语句,怎样表示。难点在于怎样处理嵌套循环和循环与分支的相互嵌套关系。下面就本章的重点及难点作一些必要说明:
        首先必须清楚三种循环的表示形式有什么不同:
        ■当型循环,也叫while循环,含义为当条件成立时执行循环,直到条件不成立为止。
        当型循环的特点是:先判断,后执行,循环语句有可能一次都不执行。
        ■直到型循环,也叫D0_while循环,含义为执行循环语句,直到条件成立为止。
        直到型循环的特点是:先执行,判断,循环语句至少执行一次。
        ■for循环,也是与当型循环类似的一种循环,但比当型循环更为灵活。一般形式为:for(表达式1;表达式2;表达式3)。循环语句其它与当型循环一致。例:分别用当型循环,直到型循环和for循环编程。
        求:
        100
        S= Σi
        i=1
        (1)用While循环
        main( )
        {
        int i,S;
        S=0;
        i=1;
        While(i<=100){
        S=S+i;
        i=i+1;
        }
        Printf("%d",S);
        }
        (2)用DO_While循环
        main( )
        {
        int i,S;
        S=0;
        i=1;
        do {
        S=S+i;
        i=i+1;
        }While (i<=100);
        Printf("%d",S);
        }
        (3) 用for循环
        main( )
        {
        int i,s=0;
        for (i=1;i<=100;i++) S=S+i;
        Printf("%d",S);
        }
        接着要说明的是循环与循环之间怎样嵌套,即多层循环怎样用,三种循环可以相互嵌套,但是必须注意,嵌套循环必须完全包含,不可能或不应有交叉的地方。
        另外,循环语句中可以包含判断结构,判断结构中也可以包含循环。举例见原书。
        关于循环这一章,一定要好好掌握它,大型程序的设计离不开循环语句,它是C程序设计的重点章节之一,建议多编写这方面的程序和上机调试,为以后的程序设计打下良好的基础。
        
        第六章   数  组
        
        ? 要求知道数组的定义和引用,并了解数组不是C语言中的基本数据类型,而是一种构造类型,即数组类型。另外搞清楚多维数组的无素在计算机内存中的排列方式,并要知道什么样的数组才能进行初绐化,单独的数组元素怎样引用。字符串与字符数组有什么区别,怎样处理字符串,本章的重点应为数组的引用、字符串的处理、用数组编写C程序、解决基本的问题。
        现将本章的重点及难点简单说明:
        数组的定义:
        数组类型数组名[维数][维数]…
        如:
        int a[10]
        char b[10][10]
        定义了a为一个整型数组,有10个元素,这10个元素是:
        a[0],a[1],a[2],a[3],……,a[9],数组元素中必须用中括号。
        定义了b为一个字符型数组,有10×10=100个元素。
        即从b[0][0],b[0][1]…,b[9][9]共100个元素。
        a为一个一维数组,b为一个二维数组。
        数组中的元数可以直接引用。
        如:a[0]+a[7]+a[5]/a[3]是合法的。
        数组可以在定义时赋值,称为初始化,但必须说明为静态存储和外部存储类数组,否者不能初始化,只能在程序中赋值。
        字符数组与字符串很相似,但字符串有一个结束标志‘\o',而字符数组不一定有,字符串用双引号表示,字符数组中的元素只能用单引号表示。
        怎样用数组编写程序,可参考P:87例6.4和P.88例6.5 P.90
        例6.6。
        其它象字符串中的一些库函数只要能知道它是干什么的,怎样引用它就可以了。
        
        第七章   函   数
        
        函数是C语言中的重点章节。C语言的程序是由一个主函数和若干个其它函数构成,其它函数可以是标准的库函数和用户自己编写的函数。C语言的这种结构便于程序实现模块化,可移植性好。
        在本章中,要求学会怎样调用标准的库函数,用户自己怎样编写函数。C语言中的函数除主函数外,其它任何函数都可以相互调用,并都可以被主函数调用。要求了解无参函数和有参函数的定义。形式参数和实际参数是怎样回事,函数的值怎样返回,是否一定要有返回值,函数的嵌套调用怎样进行,什么是局部变量和全局变量,什么是动态存储变量和静态存储变量,什么是内部函数和外部函数。
        本章的重点在于函数的定义和调用,形参与实参及其对应关系,局部变量与全局变量,动态存储变量与静态存储变量,内部函数和外部函数。难点在于动态存储变量与静态存储变量。这里对形参和实参作一些必要说明,以利于函数的调用。
        形式参数为有参函数中定义的参数,在未进行函数调用时,不占内存单元,调用时,才分配内存单元,调用结束后,所占用的内存单元被释放。
        实际参数是具体的常量、变量或表达式,他们有具体的值,占有内存单元,在函数调用时,将实参代替函数中的形式参数,即发生函数调用。但是函数调用中,要求形参与实参在类型、个数、顺序三方面一一对应,调用的方式为:函数名(实际参数),即用实参代替定义时的形参。如果在调用函数时,要返回函数值,则必须在函数定义中有return语句。如果明确表示“不返回值”可以用“Void”定义函数的类型,即“无类型”或称为“空类型”。
        另外,函数可以嵌套调用,即一个函数可以调用另一个函数。另一个函数又可以调用其它函数。如果在嵌套调用中,函数间接或直接地调用自己,称为函数的间接递归调用或直接递归调用。
        在一个函数内部定义的变量为内部变量,它只在本函数范围内有效,在此函数以外是不能使用这些变量的,这种变量我们称为局部变量。在这里,应特别注意,主程序(主函数)中定义的变量也只能在主函数中使用,而不能在其它函数中使用,这是和其它高级语言不同的。
        不同的函数中可以使用同名的局部变量,它们互不发生干扰,另外,形式参数也是局部变量。在一个函数的外面定义的变量称为外部变量,外部变量为全局变量,它的作用范围是从定义该变量的位置开始到本原文件结束。
        全局变量和局部变量是从变量的作用来分的,若从变量值存在的时间角度来分,变量可分为静态存储变量和动态存储变量。所谓静态存储方式是指在程序运行期间分配固定的存储空间的方式,而动态存储方式是指在程序运行期间根据需要进行动态的分配存储空间的方式。静态存储和动态存储具体包含四种:自动的(auto),静态(static),寄存器的(yepister),外部的(extern)。存储类别方面较难理解,必须多花一点时间搞清楚,具体的说明请参看书上P136的存储类别小结,作为本章的结束,请看下例:
        例:指出下列程序中各变量的存储属性,并写出程序的执行结果。
        #include "stdio.h"
        int i=1;
        main()
        {
        int i,j;
        i=reset();
        for(j=1;j<=3;j++){
        printf("i=%d;j=%d;\n",i,j);
        printf("(i)=%d\n",next(i));
        printf("last(i)=%d\n",last(i));
        printf("new(i+j)=%d\n",new(i+j));
        }
        }
        
        int reset()
        {
        return(i);
        }
        
        int next(i)
        {
        int j;
        return(i=i++);
        }
        
        int last(j)
        int j;
        {
        static int i=10;
        return(i=i--);
        }
        
        int new(i)
        int i;
        {
        int j=10;
        return(i=j+=i);
        }
        
        执行结果为:i=1;j=1;
        (i)=1last(j)=10new(i+i)=12i=1;
        j=2(i)=2last(i)=10new(i+j)=13i=1;j=3;
        (i)=3last(i)=10new(i+j)=1