第一章 C 程序的框架结构

  计算机系统是由硬件系统和软件系统构成,硬件由IBM、HP、DLL、Acer以及联想这样的工厂制造出来,叫裸机。软件由微软、Oracle以及用友等公司的程序员用计算机语言编写出来的,叫程序。程序和编写该程序的文档一起构成了软件系统。裸机安装上了操作系统就构成了第一层虚拟机,计算机软硬件资源由操作系统来管理。在操作系统之上安装应用软件,就构成第二层虚拟机,用户一般与这层计算机打交道的。应用软件提供一个输入数据的界面,用户通过这个界面将要加工的原始数据(文字、数值 、声音、图片、图形、动画、电影等)输入给这个软件,然后它的加工部分对数据进行交换,最后将计算结果输出给用户。

  软件是被记录下来的思维的逻辑推理过程,是计算机硬件的“学历”,是计算机系统的灵魂。没有安装软件的裸机就像是一台接收不到电视信号的电视一样,其“本事”不能被挖掘出来。因此,硬件的价值与安装到其上的软件功能成正比,人们借助软件间接地让硬件“劳动”。

  计算机硬件的制造现在可以实现批量生产了,但软件的开发还需要人来写,目前还不能现让计算机来编写软件。用来写软件的计算机语言多达2000种,C语言能在众多计算机语言中脱颖而出,靠的就是既能写系统软软件又能写应用软件的特长。系统软件一般直接与硬件打交道,编写难度大。应用软件是用来解决特定领域的数据加工问题的。现在通常的做法是用C开发一个系统软件,然后用这个系统软件开发应用软件例如用C开发出的VFP 系统,用户使用VFP 系统开发其领域的应用软件,如考场安排系统,成绩管理系统以及售票系统等。

 

1.1 用C 写程序的思想框架

 

    下面给出第一个C程序,命名为c1.c, C源程序的扩名为c。

  main ()   // main函数的功能是调用其他函数,给其传递数据,并接收传来的数据。

  {void drawsomething (char, int);   // 对该函数进行声明

  drawsomething (' ', 2); 

  drawsomething('#', 1); printf ("\n");   //括号内参数是实参

  drawsomething(' ', 1); 

  drawspmething('#', 3); printf ("\n")  //实参之间用逗号分隔

  drawsomething(' ', 0); 

  drawsomething ('#', 5); printf("\n");   //字符常量用单括撇括起

 }  // 分号是语句的标志,少了它就变成了表达式了。

  /*下面函数的功能是输出n个由参数c 指定的字符*/

  viod drawsomething(char c, int n)  // c 和 n 是形式参数,简称为形参,char 即 character.

  { int i;   // 定义变量i 是integer 型, C规定变量必须先定义,后使用。

  for (i=0; i<n; i++) printf ("c%", c)  //循环n次,每次输出一个字符。

}  // for 循环中的分号是分隔符。第三章会讲到,同一符号在不同上下文中含义迥然。

  由/*&*/ 括起来的部分是C和C++的多行注释语句,// 是C++的注释语句。本书所有程序都是在Turbo C++3.0 IDE (Integrated Development Evironment)下调试和编译。注释语句也叫内部文档,一般分两种:一种是放在源码的最前面,对该程序进行整体注释,介绍其功能,I/O接口中参数的含义、权限要求、算法的简单描述,调用了哪些函数、编者、创建日期、修改日期以及审核日期;另一种是行间注释,对可能引起歧义的语句进行说明。

  该程序由main和drawingsomething 两个函数构成,每个函数完成一个特定的功能。 一个程序有且只有一个main 函数,mian函数是程序的入口函数,其位置任意。程序总是从main函数开始执行, main 函数执行完了,整个程序也就执行完了。所以,在拿到一个要进行数据变换的问题后,首先要做的就是将要解决的问题分解为若干功能单一的函数,以使每个函数都有“一技之长”。接下来main函数按照输入数据、变换数据、输出数据的顺序调用相应的函数,由其完成具体的任务。当然函数之间可以相互调用,但不能调用main函数。这就是用C解决数据加工问题的思维框架。

  通过上面的示例可以看出,一个函数由函数首部和用花括号括起来的函数体两部分组成。

  函数首部由函数返回值类型、函数名(类型  形参、类型  形参,...)构成。 上面的main函数没有形参,叫无参函数,没有返回值类型,默认为int 型,没有返回值语句,则执行完该函数后系统返回一个随机数。 drawsomething 函数的返回值类型是viod, 说明不能有返回值。 形参是定义函数时的形式参数,必须是变量,实参是调用函数时的实际参数,必须有明确的值。 调用时,按从右到左(TC++是这样的,但Fortran等语言按从左向右的顺序)的顺序将实参的值赋给形参,所以要求实参和形参的类型、个数、顺序要对称,否则是编译不通过,或者是计算结果不正确。现在让我们故意犯个错误,将main函数第3行的,2去掉,编译后给出“too few parameters in call to 'drawsomething' in function main”的提示,将实参2改为2.9 ,运行结果不变,因为形参n是整数,它只要整数的部分。

  函数内要先写局部变量定义,被调函数声明、判断、循环、赋值、 return等语句。函数体也可以是空语句,即只有一个函数首部后跟一对花括号,叫空函数。根据C的思维框架结构,我们在处理一个大的任务时,首先要做的是将大任务分解为小任务,如此分解下去,直到每个小任务只有一个功能,我们暂时就用这个空函数代表这个小任务,待分解合理了,任务之间的关系理顺了,再把空函数为真正的函数,然后上机调试、编译为EXE文件,就可以作为软件使用了。如,求两个正整数的最大公约数和最小公倍数,可以分解为如下三个函数:

  int max(int, int){}  // max 函数负责求最大公约数,返回值类型是int型。

  min (int, int) {}  // min 函数负责最小公倍数,省略了返回值类型。

  void main () {}  //负责接收待求数,调用max和min,接收其返回值,最后输出结果。

  下面细化后的三个函数:

  int  max (int  bcs, int  cs)  // bcs是被除数的拼音字母缩写,cs代表除数。

  { int yushu;   // yushu代表余数,zdgys 代表最大公约数,zxgbs代表最小公倍数。

  while(cs != 0)  // while 循环结构,读作当条件为真时,执行循环体,然后返回到

    {yushu=bcs/cs;  //条件处继续判断,当条件仍为真时,继续执行循环体,如此循环,

    cs=yushu;  //当条件为假时,跳出循环,继续执行后继语句

    bcs=cs;  //当循环体语句多于一条时用{}括起来,构成复合语句;其功能相当于一条语句。

    }  // 下面的return语句是上面循环结构的后继语句

  return (bcs):  // 将加工结果返回调用处, return后面的圆括号可以省略。

}  //将源代码写成锯齿状的目的是增强程序的可读性,程序的结构层次清淅,一目了然。

  min(int bcs, int   cs)// 形参是局部变量,与生活中的临时工类似。 

  {int zdgys=max(bcs, cs);  // 在定义局部变量的同时调用max函数,其返回值赋之。 

  return  bcs*cs / zdgys;   // 先计算,在返回结果。

}

  void  main ()  // 无参函数,且不能有返回值,有返回值也不允许使用它。

  {  int  bcs=26,  cs=4;  // 定义局部变量的同时,给其赋值。

    int   zdgys=max (bcs, cs)  // 调用max 函数,并将其返回值给zdgys。

    int  zxgbs=min (bsc, cs)  // 调用min函数,并将其返回值给zxgbs。

    printf ("\n max=%d, min=%d\n", zdgys,zxgbs);   //输出结果

    return 6;   // 6可略,写上此句不算错,读者可编写一个使用此返回值的程序,编译。

}   // 时会给出“not an allowed typ in function function name”的提示。

  在TC++系统的IDE中输入源码,保存为c2.c, 也可以在类似于NotePad这样的文本文件编缉器中输入源码,然后在TC++系统中打开,编译通过后,生成c2.obj(Object)二进制码文件,再和库函数Link成可独立执行的c2.exe(execute)机器码文件。 可以到XP的命令提示符下运行,输入结果为Max=2, Min=52。 

  为什么要把具有某一功能的一段程序称为函数呢? 仔细剖析Max这段程序可以看出,其功能是将传进来的两个正整数(自变量、其所有可能的值构成定义域)经过一定的变换规则加工为最大公约数(因变量,其所有的值构成的值域), 称这段程序为函数是比较恰当的。 

  若被调用函数处于调用函数之前,或函数的返回值类型是int型,声明语句可以省略。上面的min函数直接调用了max函数就是这个原因。函数的声明,也可以直接写在所有函数之前,程序的开始部分。下面的程序c3,c就是这样。

  long power (int, int);   // 函数的声明放在所有函数之前,可不必声明而直接调用。

  void  main  () 

  {  int a=2,  b=8;  // 16位计算机int型变量占2字节,取值范围-32768~32767

    printf ("result is:%ld\n", power (a,b);  // %ld表示此处放一个长整型数,l 即long

} //d 即decimal, \n 表示 换行。 

  long power (int x, int n)      // long int 型中的int 可省略,该函数的功能是求X

  {int  i ;  // 定义局部变量,也叫自动变量,用auto修饰,可以省略。

  long  result=1;   // 16 位计算机int型变量占4个字节,取值范围-2147483648~2147483647 。

  for (i=0; i<n; i ++)  // for 循环结构,循环n次。

    result*=x   // 与result = result*x等价,但前者更简洁。

        return result; 

}

  为何printf函数没有声明,也不见其函数体,用户也没有编写这个函数,却可以拿来直接调用呢?原因是它是库函数。库函数是由C系统开发人员事先写好并编译成二进制代码后保存到库文件中,并随系统一起提供的。库函数一般完成和硬件直接打交道的底层功能,或复杂计算。前结如输出结果的printf函数,将键盘输入的数据送入到内存指定的scanf 函数, 清屏函数clrscr 等, 后者如sqrt、 sin 函数等。由于printf、 scanf 、 clrscr 函数使用频繁, TC++编译系统直接使用它们而不必声明,其他函数在使用前仍需声明,但与用户自定义函数的声明方法略有不同,程序c4.c 说明了这种差异。 

  #include <math.h>  // 包含命令,下面三个库函数首部信息保存在这个头文件中。

  viod ,main()   // 库函数sin ()、 log10 ()   , sqrt () 的返回结果作为printf () 的实参。

  {printf ("sin(3)=%lf,"sin(3))  // 求3的正弦, l 即是long, f 即 float。

  printf ("log10(10)  = %lf," ,log10(10)  )  //求 log10(10) ,    lf 表示双精度数。

  printf ("5's square root is = %lf\n", sqrt (5));   // 求5的平方根

}  // 输出结果为sin(3)= 0.14120, ,log10(10)   =1.000000, 5's square root is:2.236068 。

  # include <math.h> 是编译预处理命令,编译预处理命令(不能叫语句)不是C 语言的组成部分,但却是C编译系统的重要组成部分,使用它主要是为了提高程序的可读性。所有编译预处理命令都以#号开头且后面紧跟define, undef,  pragma 等命令,命令结束处勿写分号。

  在正式编译这段程序前,编译预处理程序找到math.h文件(该文件是一个文本文件,保存在安装文件下include 文件夹中),并将其内容复制一份粘贴到该命令处以取代该命令,然后编译器对经编译预处理后的程序进行编译、连接、生成可执行文件。 *. h (h 即head)文件一般称为头文件,这些文件中包含有库函数的首部信息和系统定义的常量、类型等。程序中用到库函数时,须将相应的头文件写入程序的开始部分,以便在进行编译预处理时,将这些文件的内容包含进来。 

  顺便提一下,<math.h>也可以写作“math.h”。 用双撇时,编译处理程序先去当前文件所在的文件夹下寻找,若找不到,再到系统配置文件所指定的文件夹中查找,查找用户自定义文件一般采用这种方法。用尖括号时,编译预处理程序直接到系统配置文件所指定的文件夹中查找,查找系统自带的文件一般采用这种方法。 

 

 

posted @ 2018-01-09 22:03  ZQXTXK  阅读(832)  评论(0编辑  收藏  举报