3.1 构建组件
类型允许我们指定如何利用特定位的各种操作,函数允许我们指定在数据上所进行的操作。
利用C结构(structure)类型把各种不同类型的信息集成在一起,并利用指针(pointer)间接引用这些信息。
在C语言中,程序都是由几种基本数据类型构建的:
· 整数(ints)
· 浮点数(floats)
· 字符(chars)
定义3.1 数据类型是值的集合和在这些值上的操作集。
操作关联类型,反之不成立。当我们执行一个操作时,我们必须确保操作数和结果有正确类型。忽略这一点是程序设计的常见错误。在某些情况下,C语言程序可以进行隐式的类型转换,而在其它一些情况下,我们利用显式的类型转换(cast)。
例如,如果x和N是整数,则表达式:((float)x)/N中包含两种类型转换,(float)是一种显式转换,把x的值转换为浮点类型,接着用C语言中隐式类型转换规则对N进行隐式转换,使除法操作符的两个参数都变味浮点数。
数据类型的概念不局限于内置的整数、浮点数和字符类型。作为一种组织软件的高效方式,我们还定义自己的数据类型。
当定义C语言中的一个简单函数时,就高效地创造了一种新的数据类型,其中函数实现的操作添加到用该函数参数表示的数据类型定义的操作中。
实际上,每个C程序在某种意义上都是一种数据类型,也就是说,是数值集合(内置类型或其它类型)及其相关操作(函数)的一个列表。
程序3.1 函数定义
#include <stdio.h>
int lg(int);
int main()
{
int i,N;
for(i=1,N=10;i<=6;i++,N*=10)
{
printf("%7d %2d %9d \n",N,lg(N),N*lg(N));
}
}
int lg(int N)
{
int i;
for(i=0;N>0;i++,N/=2);
return i;
}
在C语言中用于实现对数据进行新操作的机制就是函数定义。
所有函数都有一个参数列表和可能的一个返回值。
通过给出函数名和它的返回值类型来说明(declare)函数。
在函数定义中,对变量命名(称为参数),并根据这些名字表达计算,把它们作为局部变量看待。
当调用函数时,这些变量初始化为参数传递的值,接着执行函数代码。
return语句是函数执行结束的指令,它还向调用函数返回值。
原则上,调用函数不受其他函数的影响,但也有很多例外情况。
定义和声明的分开为组织程序提供了灵活性。
在写程序时,目标是组成程序,以使重用旧程序解决新问题。
首先,通过仔细了解和准确确定程序所使用的操作,可以容易地把它扩展到支持这些操作的任意的数据类型上。
其次,通过仔细了解和准确确定程序的作用,可以把它执行的抽象操作添加到求解新问题所用的操作中。
程序3.2 数字类型
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
typedef int Number;
Number randNum()
{
return rand();
}
main (int argc,char *argv[])
{
int i,N=atoi(argv[1]);
float m1=0.0,m2=0.0;
Number x;
for(i=0;i<N;i++)
{
x=randNum();
m1+=((float)x)/N;
m2+=((float)x*x)/N;
}
printf("Average:%lf \n",m1);
printf("Std.deviation:%lf \n",sqrt(m2-m1*m1));
}
备受推荐的软件工程实践是把程序分为三个文件:
· 一个接口(interface),定义了数据结构以及声明用于操作这个数据结构的函数。
· 在接口中声明的函数的一个实现(implementation)。
· 一个客户(client)程序,调用接口中声明的函数,以便在更高抽象层次上使用。
结构体是集合类型,用于定义数据的集合,以便将整个集合作为一个单元来操纵,但仍可以通过一个给定数据集的个体成员的名字来引用它。