1
C语言程序设计
1.7.9 6.9 程序举例

6.9 程序举例

1.复数的表示和处理

C 语言提供了许多数值类型,可供人们在处理数据时使用。但这里的数值类型也不完全,例如就缺乏一个复数类型。假设我们要写一个处理复数数据的程序,那应该怎么办呢?我们当然可以直接用两个double 表示一个复数去完成这种程序,例如定义函数:

  addcomplex(double r1,double i1,double r2,double i2)

这样不但很难返回结果,调用时也需要时时记住各个参数的位置。程序写起来会很麻烦。

应注意到,这里的一个复数就是一个逻辑上的数据体,应该定义为一个类型,而后再定义一批以复数类型为操作对象的函数。在完成了这些工作之后,再写程序的其他部分就会变得清晰简单了。人们在程序设计实践中逐步认识到,在设计实现一个比较复杂的程序时,最重要的一个步骤就是找出程序里所需的一批数据类型,将它们的结构和有关功能分析清楚,设计并予以实现。而后在这些类型的基础上实现整个程序。这样做,得到的程序将更清晰,其中各个部分的功能划分比较明确,更容易理解,也更容易修改。

现在我们就具体考虑复数类型的实现。我们选择平面坐标表示,即将一个复数表示为一个实部和一个虚部。针对通常的复数运算,可以考虑用两个double 类型的值表示一个复数。

应该采用什么机制将这两部分结合起来呢?由于这两部分的类型相同,我们可以考虑用具有两个double 元素的数组,或者用具有两个double 成员的结构。由于需要定义许多运算,用结构表示有利于将复数作为参数传递或者作为结果返回,因此有下面定义:

  typedef struct {

  double re,im;

  } Complex;

下面就可以考虑基于这个类型的各种运算了。

由于复数类型数据对象里的数据项很少,我们可以考虑直接传递Complex 类型的值和结果,这样可以避免复杂的存储管理问题。考虑最基本的算术函数,它们的原型应是:

  Complex addComplex(Complex x,Complex y);

  Complex subComplex(Complex x,Complex y);

  Complex multiComplex(Complex x,Complex y);

  Complex divComplex(Complex x,Complex y);

还需要考虑如何构造复数。我们不希望在使用复数的程序里直接访问Complex 类型的成分,如果人们经常需要那样做,程序里的错误将很难控制和查找。如果对复数的所有使用都是经过我们定义的函数,只要这些函数的定义正确,程序里对复数的正确使用也就有保证了。下面是几个构造函数:

  Complex mkComplex(double re,double im);

  Complex d2Complex(double d);

  Complex n2Complex(int n);

后两个函数可以看做由double 和int 到复数的数值转换函数,定义它们是为了使用方便:

Complex mkComplex(double r,double i) {

Complex c;

c.re=r;

c.im=i;

return c;

}

Complex d2Complex(double d) {

Complex c;

c.re=d;

c.im=0;

return c;

}

Complex n2Complex(int n) {

Complex c;

c.re=n;

c.im=0;

return c;

}

下面是加法函数的定义:

Complex addComplex(Complex x,Complex y) {

Complex c;

c.re=x.re+y.re;

c.im=x.im+y.im;

return c;

}

减法函数与此类似。

Complex subComplex(Complex x,Complex y) {

Complex c;

c.re=x.re−y.re;

c.im=x.im−y.im;

return c;

}

乘法和除法函数的算法复杂一点,根据数学定义也不难给出。

img566

有了这些函数之后,就可以很方便地写各种复数计算了,请读者自己完成。这样就完成了一个基本的复数计算程序包,基于它已经可以做许多有用的事情了,甚至还可以扩充这个程序包的功能,增加其他有用的函数。

2.把一个整数按大小顺序插入已排好序的数组中

为了把一个数按大小插入已排好序的数组中,应首先确定排序是从大到小还是从小到大进行的。设排序是从大到小进行的,则可把欲插入的数与数组中各数逐个比较,当找到第一个比插入数小的元素i时,该元素之前即为插入位置。然后从数组最后一个元素开始到该元素为止,逐个后移一个单元。最后把插入数赋予第i个元素即可。如果被插入数比所有的元素值都小则插入最后位置。

例6-24 把一个整数按大小顺序插入已排好序的数组中。

img567

img568

本程序首先对数组a中的10个数从大到小排序并输出排序结果。然后输入要插入的整数n。再用一个for语句把n和数组元素逐个比较,如果发现有n〉a[i]时,则由一个内循环把i以下各元素值顺次后移一个单元,后移应从后向前进行(从a[9]开始到a[i]为止)。后移结束跳出外循环。插入点为i,把n赋予a[i]即可。如所有的元素均大于被插入数,则并未进行过后移工作。此时i=10,结果是把n赋于a[10]。最后一个循环输出插入数后的数组各元素值。程序运行时,输入数47。从结果中可以看出47已插入到54和28之间。

3.输入五个国家的名称并按字母顺序排列输出

本题编程思路如下:五个国家名应由一个二维字符数组来处理。然而C语言规定可以把一个二维数组当成多个一维数组处理,因此本题又可以按五个一维数组处理。每一个一维数组就是一个国家名字符串,用字符串比较函数比较各一维数组的大小,并排序,输出结果即可,编程如下:

例6-25 输入五个国家的名称并按字母顺序排列输出。

img569

img570

本程序的第一个for语句中,用gets函数输入五个国家名字符串。C语言允许把一个二维数组按多个一维数组处理,本程序说明cs[5][20]为二维字符数组,可分为五个一维数组cs[0]、cs[1]、cs[2]、cs[3]、cs[4]。因此在gets函数中使用cs[i]是合法的。在排序子函数中的for语句中又嵌套了一个for语句组成双重循环,这个双重循环完成按字母顺序排序的工作。在外层循环中把字符数组cs[i]中的国名字符串拷贝到数组st中,并把下标i赋予p。进入内层循环后,把st与cs[i]以后的各字符串作比较,若有比st小者则把该字符串拷贝到st中,并把其下标赋予p。内循环完成后如p不等于i说明有比cs[i]更小的字符串出现,因此交换cs[i]和st的内容。至此已确定了数组cs的第i号元素的排序值。然后输出该字符串。在外循环全部完成之后即完成全部排序和输出。