一个C语言的基本结构,c语言定义一个学生结构体
大多数计算机操作模拟真实世界。如果要用计算机模拟现实世界,就需要使用数据抽象的方法。抽象是指提取人、事、物、概念所关注的共同特征,忽略非本质的细节,用各种概念准确描述这些特征,使这些概念构成某种描述现实世界的模型。
以数学中的复数为例,通过结构解释数据类型的组合和抽象。至于流程抽象,我们见过的最简单的形式就是用函数名封装一组语句,作为一个整体使用。
现在我们用C语言来表示一个复数。如果复数是由直角坐标系中的实部和虚部组成,如果复数是由极坐标中的模和自变量组成,那么这两个坐标系是可以相互转换的。如下图所示
例如,如果用实部和虚部来表示一个复数,我们可以采用由两个double类型组成的结构:
struct complex_struct { doublex,y;};
这定义了标识符complex_struct。既然是标识符,那么它的命名规则和变量是一样的,但是它代表的不是变量,而是类型,struct complex_struct {double x,y;}整体可以看作是一个类型名,就像int或者double一样,只不过是复合类型。如果这个类型名用于定义变量,可以这样写:
struct complex_struct { doublex,y;} z1,z2;
这样,z1和z2是两个变量名,后面是一个变量定义;不。必须注意的是,结构的定义很少;这是初学者常犯的错误。不管是上面两种形式中的哪一种定义了标识符complex_struct,以后都可以直接用struct complex_struct代替类型名。例如,另外两个复变量可以定义如下:
struct complex_struct z3,Z4;
结构变量也可以在定义时初始化,例如:
struct complex_struct z={ 3.0,4.0 };
复数加法的算法是实部和实部相加,虚部和虚部相加。复杂加法运算的功能代码如下:
struct complex _ struct add _ complex(struct complex _ struct Z1,struct complex _ struct z2){ Z1 . x=Z1 . x z2 . x;Z1 . y=Z1 . y z2 . y;returnz1}
此外,我们还提供了一个函数来构造复杂变量:
struct complex _ struct make(double x,double y){ struct complex _ struct z;z.x=xz.y=yreturnz}
现在让我们实现一个完整的复数运算程序。在上一节中,我们已经定义了复数的结构。现在我们需要围绕它定义一些函数。复数可以用直角坐标表示,也可以用极坐标表示。在直角坐标中加减,在极坐标中乘除更方便。如果我们定义的复杂结构是在直角坐标下,那么就要提供极坐标的转换函数,以便需要时可以方便地取其模数和振幅:
struct complex_struct { doublex,y;};double real _ part(struct complex _ struct z){ returnz . x;} double img _ part(struct complex _ struct z){ returnz . y;} double magnitude(struct complex _ struct z){ return sqrt(z . x * z . x z . y * z . y);} double angle(struct complex _ struct z){ double pi=acos(-1.0);if(z . x 0)return tan(z . y/z . x);否则return tan(z . y/z . x)PI;}
此外,我们还提供了一个函数,可以提供极坐标来构造复变量,并在函数中自动进行相应的转换然后返回构造的复变量:
struct complex _ struct make _ from _ mag _ ang(double r,double A){ struct complex _ struct z;z . x=r * cos(A);z . y=r * sin(A);returnz}
在此基础上,可以实现复数的加、减、乘、除:
struct complex _ struct add _ complex(struct complex _ struct Z1,struct complex _ struct z2){ returnmake _ from _ real _ img(real _ part(Z1)real _ part(z2),img _ part(Z1)img _ part(z2));} struct complex _ struct sub _ complex(struct complex _ struct Z1,struct complex _ struct z2){ return make _ from _ real _ img(real _ part(Z1)-real _ part(z2),img _ part(Z1)-img _ part(z2));} struct complex _ struct mul _ complex(struct complex _ struct Z1,struct complex _ struct z2){ returnmake _ from _ mag _ ang(magnitude(Z1)* magnitude(z2),angle(Z1)angle(z2));} struct complex _ struct div _ complex(struct complex _ struct Z1,struct complex _ struct z2){ return make _ from _ mag _ ang(magnitude(Z1)/magnitude(z2),angle(Z1)-angle(z2));}
可以看出,复数加减乘除运算的实现并不是直接访问结构complex_struct的成员X和Y,而是将其作为一个整体来对待,通过调用相关函数来获取其直角坐标和极坐标。这样,替换结构complex_struct的存储表示就非常方便了,比如用极坐标代替:
struct complex_struct { doubler,A;};double real _ part(struct complex _ struct z){ returnz . r * cos(z . A);} double img _ part(struct complex _ struct z){ returnz . r * sin(z . A);} double magnitude(struct complex _ struct z){ returnz . r;} double angle(struct complex _ struct z){ returnz。a;} struct complex _ struct make _ from _ real _ img(double x,double y){ struct complex _ struct z;double pi=acos(-1.0);z . r=sqrt(x * x y * y);if(x ^ 0)z . A=atan(y/x);else z . A=atan(y/x)PI;returnz} struct complex _ struct make _ from _ mag _ ang(double r,double A){ struct complex _ struct z;z.r=rz . A=Areturnz}
虽然改变了结构complex_struct的存储表示,但是add_complex、sub_complex、mul_complex、div_complex等复杂运算的函数仍然可以使用,不需要做任何改变。原因是这些函数只是把结构complex_struct作为一个整体使用,而不直接访问它的成员,所以不依赖于它有哪些成员。下面就用下图详细分析一下吧。
这里要介绍的编程思想叫做抽象。其实“抽象”这个概念并没有那么抽象。简单来说就是“提取公因子”:ab ac=a(b c)。如果改变A,ab和ac都需要改变,但如果写成a(b c)的形式,只需要改变其中一个因子。
在我们的复数运算程序中,复数可能用直角坐标表示,也可能用极坐标表示。我们提取这个可变因子组成复数存储表示层:real_part,img_part,magnitude,angle,make_from_real_img,make_from_mag_ang。这一层看到的是数据是结构的两个成员X和Y,或者R和a,如果你改变了结构的实现,你就得改变这一层的函数的实现,但是函数接口不变,所以调用这一层的函数接口的复杂操作层也不需要改变。复数运算层看到的数据只是一个“复数”的抽象概念。知道它有直角坐标和极坐标,就可以调用复数存储表示层的函数来获取这些坐标。再往上看,其他使用复数运算的程序,把数据看成是更抽象的“复数”概念,只知道它是一个数,可以像整数和小数一样加减乘除,即使它有直角坐标和极坐标。
这里把复数存储表示层和复数运算层称为抽象层。自下而上,“复数”的数据越来越抽象。将所有这些层组合在一起就是一个完整的系统。组合使系统任意复杂,而抽象使系统的复杂性可控,任何变化都限制在某一层而不影响整个系统。
我们通过一个复杂存储表示抽象层将complex_struct结构的存储格式与上层的复杂操作函数分离开来。complex_struct结构可以存储在直角坐标或极坐标中。但有时需要同时支持两种存储格式。比如之前已经采集了一些数据并存储在计算机中,有些数据存储在极坐标中,有些数据存储在直角坐标中。如果想把这些数据都存储在complex_struct结构中,该怎么做?一种方式是complex_struct结构采用直角坐标格式,直角坐标的数据可以直接存储在complex_struct结构中,极坐标的数据先用make_from_mag_ang函数转换成直角坐标再存储,但是转换总会损失精度。这里还有一个方法。complex_struct结构由一个数据类型标志和两个浮点数组成。如果数据类型标志为0,则两个浮点数代表直角坐标;如果数据类型标志为1,则两个浮点数表示极坐标。以这种方式,直角坐标和极坐标的数据可以适应到complex_struct结构中,而没有转换和精度损失:
enum coordinate _ type { RECTANGULAR,POLAR };struct complex _ struct { enum coordinate _ type t;doublea,b;};
enum关键字的作用类似于struct关键字的作用。标识符coordinate_type被定义为一个类型,只是struct complex_struct指示一个结构类型,而enum coordinate_type指示一个枚举类型。枚举的成员是常数,它们的值由编译器自动赋值。比如上面的枚举类型定义后,矩形表示常数0,极坐标表示常数1。如果不想从0开始分配,可以这样定义:
enum coordinate _ type { rectangle=1,POLAR };
这样,RECTANGULAR表示常数1,POLAR表示常数2,这些常数的类型为int。需要注意的是,结构的成员名和变量名不在同一个命名空间,但是枚举的成员名和变量名在同一个命名空间,所以会有命名冲突。例如,这是非法的:
int main(void){ enum coordinate _ type { rectangle=1,POLAR };不规则的;printf(%d%d\n ,矩形,极坐标);return0}
当complex_struct结构的格式发生变化时,需要修改复杂存储表示层的函数,但只要函数接口不变,就不会影响上层的函数。例如:
struct complex _ struct make _ from _ real _ img(double x,double y){ struct complex _ struct z;z.t=矩形;z . a=x;z . b=y;returnz} struct complex _ struct make _ from _ mag _ ang(双r,双A)
{
struct complex _ struct z;
z.t=极坐标;
z . a=r;
z . b=A;
returnz
}
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。