第二十二章? 结构 22.1 面向对象的启蒙 22.2 结构/struct 的定义 22.3 ?. 操作符 22.4 -> 操作符 22.5 结构实例 22.6 结构与函数 ? 22.6.1 结构变量作为函数参数 ??? 22.6.1.1 结构变量以传值的方式传递 ??? 22.6.1.2 结构变量以传址的方式传递 ??? 22.6.1.3 结构变量以常量传址方式传递 ??? 22.6.1.4 兼容C:使用指针传递结构变量 ? 22.6.2 函数返回值是结构类型 22.7 作业   大家好。课程拖了好久,大家急,我也急。今天是周末,一早去医院体检,被女医生扎了一针,胳膊上留下一个针眼。不禁想起一个真实的故事。一个我的同行(程序员),和我差不多瘦。有一年夏天到南方出差,住在旅馆里,一个晚上没睡好!为什么?因为蚊子又太多了啊。几夜没睡好这可真够倒霉的啦。谁知祸不单行。上了火车他困啊!卧铺上一歪他就睡着了,那只胳膊瘦瘦的,从睡铺上垂下来,露出被蚊子们叮的密密麻麻的红点。才睡不久,就被乘警叫醒带走——下铺的乘客去举报了,说他是吸毒青年。 兄弟们,当程序员很苦!不过苦得值。当然身体要弄好。   22.1 面向对象的启蒙   我们以前学习了各种数据类型的变量。先来复习一下。 变量做什么用?程序用来变量来存储数据,用它来表达真实世界中的事物。   比如:假设我最近开了一家幼儿园,园里有一群小娃娃。娃娃的家长们把孩子交给我们之后,都要求我们要时时关心孩子们的“健康成长”,因此我们不得不为每个孩子建一个入园档案。档案记载每个孩子的一些数据。   //娃娃的名字: char xingMing[11];? //姓名最长5个汉字,占用10字节,多留一个字节用于存放'\0'   //娃娃的身高: int shenGao; //单位 cm   //体重: float tiZhong; //单位 公斤   我们知道,世界的万事万物,去除一些无用的修饰,可以表达为“数据”和“操作”。比如:我吃饭,“我”和“饭”是数据,而“吃”是一种动作,一种操作。对应到编程语言,就是“数据”和“流程”。那么,当我们写程序来解决某一现实问题时,应该先考虑的“数据”,还是“流程”呢?多数问题,应该先去考虑“数据”。也就是说,当遇上一个现实问题,我们应先去“抽取”这个问题的数据,考虑如在程序中表达,设计,定义这些数据。然后再去设计流程。   以我们上面的幼儿园管理的例子,我们现在已经差不多抓出相关的数据。不过,上面所做的,只是“一个孩子”的数据,幼儿园里的孩子当然不会只有一个。还好,我们学过数组,不是吗? 所以,我们将上面的变量定义改一改。   先定义一个宏,假设园里有30名宝宝。   #define BAOBAO_GESHU 30? //30个宝宝   //30个宝宝,要30个名字: char xingMing[BAOBAO_GESHU][11]; //忘了二维数组?呵呵。复习一下。   //30个宝宝,30个身高: int shenGao[BAOBAO_GESHU];   //30个宝宝,30个体重: float tiZhong[BAOBAO_GESHU];   假设我们的程序具备打印功能。这样每一天放学时,我们都在宝宝走出校门前,将他或她称量一番,得出体重,身高。记录到我们的程序,然后程序再打印出一张小纸条。贴在宝宝后脑勺,供宝宝妈妈参考……哈哈,我们可以把这个小程序卖给本市的300家幼儿园,每份卖400元,300家就是 400 * 300 = 120000元……流口水中……   擦干口水回过神,来开始我们的今天最重要的学习:面向对象的启蒙。 什么叫面向对象,我且不解释。不结合实例子,解释也没有用啊。   一个人,有眼睛、鼻子、嘴、头发、四肢。也就是说,“人”是一种“数据”,而“鼻子”,“嘴”,“头发”等也各自是一种数据,彼此之间具备不同的“数据类型”。但是在多数情况下,“人”是一种不可再分的整体(医院里负责解剖的人所做的事不在我们所说的多数情况之内)。扯到编程上而来 ,当我们想用程序管理30个人时,我们应该是定义一个数组,存储30个人,而不是分开来定义成:30个眼睛[2]、30个鼻子、30个头发[1000],30个四肢。   回到幼儿园程序。每个宝宝都有身高、体重、姓名这些属性;宝宝也应作为一个整体,而不是像上面那样分开来定义。   这就是面向对象的第一个启蒙: 面向对象,是为了让我们把程序写得更“自然而然”。越是支持面向对象的编程语言,我们就越能以接近人类自然逻辑的思路来设计程序;而越不支持面向对象的编程语言,也许它初看起来似乎很浅显易用,但当用它来解决实际问题时,程序员就不得不受限于这门语言特有的解决问题的思路。   说完面向对象的好处,我们必须马上来做几个问题的“纠偏”。   第一、面向对象并不代表解决问题的最高效率。   确实地这样的。“面向对象”被提出,是因为某些问题实在太庞大太复杂了,人类的写程序解决这些问题时,一会儿就胡涂了,晕了,搞错了,于是希望有一种方法来让程序员不会那么快在一堆代码前精神崩溃。这才提出了“面向对象”。所以在我们第一次接触到这个概念时,请先记住,和我们前面所讲的一样,比哪为什么要有变量,为什么要有数据类型:编程语言设计的不断改进,都是为了迁就人类的“容易犯错”或“天生懒惰”。否则,如果只是要追求最高效率,如果人类有着机器般的脑,编程语言根本不需要有C,C++,JAVA,C#什么的,甚至连汇编也不需要,只需要一个机器语言就可以。“面向对象”的编程思想迁就我们什么呢?它迁就人类总是喜欢以比较自然的方式来解决问题。   先来考虑,“自然而然”的事,不一定是最高效率。这很多,比如,路口的红绿灯制度,显然不是最高效率。最高效率其实应该是闯红灯。你会说,闯红灯会出车祸,出车祸不就堵车?造成效率低了?哦,其实我是要说:如果每个司机,行人都闯红灯,并且能保证不撞到其它车或行人,那么路口上车辆穿行的效率肯定最高。可惜,驾驶员做不到这一点,所以只好有红绿灯。   第二、虽然说面向对象是“很自然的事”,但我们仍然要花力去学习。   古人老子说:“道法自然”。那什么叫“自然”啊? 这里的自然也是有规定的,并不是人类的所有行为都称为“自然”的,也不一定是你习惯的行为就自然,你不习惯的行为就不自然。比如人家都觉得“饭前便后要洗手”,可若你偏要认为这种行为太不自然,太做作,那我们也没有办法。 另外,人类解决现实生活中,有时也要追求点效率。比如,酒家承办婚礼,要准备10桌一样的酒席。每一桌上面都有这道菜那道汤的。我们可以把完整的一桌酒菜看成一个“整体”。但大厨们可不这样认为,我猜他们在准备时,一定是先把某道菜一块儿做好10桌的份量,然后做下一道菜,而不是我们认为的,先办好一桌,再办下一桌。对于我们,一桌一桌菜是自然的,而对做的人来说,一道一道菜才是自然的。 如何设计一个面向对象的程序,并且保证一定的高效率,这是一门无止境的科学,我们需要不断地学习。面象对象源于生活,而高于生活。   说了这么多,大家不要被“面向对象”吓坏了。今天我们所要学习的面向对象的设计方法,很简单:把同属于一个整体的“数据”,归成一个新的类型去考虑,而不是分割成每一部分。   22.2 结构/struct 的定义   “结构”提供一种能力,允许程序员把多个数据类型,归属成一个完整的,新的数据类型。   以上面的幼儿园管理程序为例,我们可以定义出这样一个新的数据类型:   struct BaoBao { ??? char xingMing[11]; ??? int shenGao; ??? float tiZhong; }; //<----注意,以分号结束定义   现在,我们有了一种新的数据类型,叫 BaoBao 结构。该数据类型由三个变量的定义组成。分别是xingMing[10], shenGao, tiZhong。这三个组成变量,我们称为该结构的“成员变量”。   既然 BaoBao 是一种数据类型,我们就可以定义一个该类型的变量:   BaoBao daBao; //daBao 是一个“BaoBao”类型的变量。   这个过程,和我们定义一个整型变量,并无多大区别:   int a;   我们记得不同数据类型的变量,有着不同的大小(占用内存的大小)。 比如:一个bool或一个char类型的变量,占用一个字节,而一个int类型则占用4个字节的内存; 后来我们又学习数组,数组的大小除了和它的数据类型有关外,还有数组元素的个数有关。比如:char xingMing[11]占用11个字节,而int txZhong[30]占用4 * 30个字节 最后,前面的几章内,我们又学习了指针。指针类型固定占用4个字节。这是因为不管什么类型的指针,都是要用来存储一个内存地址,而在32位的计算机上,内存地址的大小固定为4字节(8 * 4 = 32位)。   这一切我们可以用 sizeof 来验证:   int s1 = sizeof(char); int s2 = sizeof(int); int s3 = sizeof(char *);   int arr[10]; int s4 = sizeof(arr);   上面的程序执行后,s1到s4分别为:1,4,4,40。   现在我们来问,我们自定义的 BaoBao 数据类型,它的大小是多少?换句话,也可以问:做出如下定义之后:   BaoBao daBao;   daBao这个变量“吃”掉了多少字节的内存呢?   我们再看一次 BaoBao 这个结构的定义:   struct BaoBao { ??? char xingMing[11]; ??? int shenGao; ??? float tiZhong; };   直观上猜测,BaoBao这个结构由三个成员变量组成,那么它的大小应该就是这三个成员变量大小之和。这个猜测颇有道理,如果它是正确的话,那么,sizeof(BaoBao) 应等于 11 + 4 + 4 = 19;   让我们打开CB6,然后新建一个控制工程。然后在 Unit1.cpp 里增加一些代码,使之看来如下(黑体部分为需要增加的代码):   //--------------------------------------------------------------------------- #pragma hdrstop #include <iostream.h>   //--------------------------------------------------------------------------- struct BaoBao { ?? char xingMing[11]; ?? int shenGao; ?? float tiZhong; };   #pragma argsused int main(int argc, char* argv[]) { ?? int size = sizeof(BaoBao);   ?? cout << "SizeOf struct BaoBao = " << size << endl;   ?? system("PAUSE");   ?? return 0; } //---------------------------------------------------------------------------   按F9后看到运行结果:  (sizeof BaoBao) 奇怪,BaoBao 结构的大小,竟然是20。比我们猜测的19,多出了一个字节?     事情是这样的。就像我们去小店买东西,假设有一天我们要去赶飞机,走之前去小店买了点食品,总价19元,店老板没有1元钱,于是我们为了能快点出发,就直接给他20元,告诉他不用找零钱了。   为了效率,编译器也会有类似的动作。这称为结构的“字节对齐”,当然,这个对齐方法要比我们的19取整到20复杂一点。关于编译器是通过什么规则来把一个结构进行扩展,我们留在本章的增补课程中。这里只需记住,对于一个结构的大小,一定要用sizeof才能得到它的实际大小。当然,可以肯定的是,结构的大小一定是大于或等于其所有成员变量的大小之和。   现在我们知道,定义了一个BaoBao的变量(daBao),就会在吃掉20个字节的内存。接下来我们来看,如何使用 daBao 这个变量呢?   22.3 ?. 操作符   通过点操作符,我们可以得以一个结构中的成员变量。 请看例子:   BaoBao daBao; //定义一个 BaoBao 类型的变量,变量名为daBao;   daBao.shenGao = 83; //“大宝”的身高是83公分 daBao.tiZhong = 14; //体重是14公斤   //字符串用strcpy函数来设置 strcpy(daBao.xingMing, "大宝");   .操作符,可以理解为“的”。不是吗? daBao.shenGao ,就读成 “大宝的身高”,多么的“自然而然”啊!今天我们已经摸到“面向对象”编程世界的门环了。   22.4 -> 操作符   结构变量也可以是一个指针:   BaoBao* pDaBao; //定义一个 指向“BaoBao”结构的指针   考一下,pDaBao占用几个字节的内存,如果你回答是:20,那真该自己绝食一顿。:(   指针的大小总是只有4个字节。指向“结构”的指针也如此。我们可以通过 new 操作符来为一个指针分配实际内存:   pDaBao = new BaoBao;   这一点,和我们定义一个int 型指针,然后为它分配内存的操作一致:   int* pInt = new int;   pInt分配后,指向一块大小为sizeof(int)的内存,而pDaBao分配后,指向一个大小为sizeof(BaoBao),的内存。   对于指向结构的指针变量,要取得相应结构内的成员变量,必须通过以下语法:   (*pDaBao).xingMing; (*pDaBao).shenGao;   从语法上分析,你必须先复习一下《指针》章节中,关于*的用法与意义。*pDaBao 得到指针实际向的结构变量,然后再进行点操作符,得到该结构变量内的某一成员。   不过上面的写法显然很繁琐。简化方法是使用 -> (可读作箭头操作符,或指向操作符,或者就叫“减大于”吧):   pDaBao->xingMing; pDaBao->shenGao;   也就是说,如果一个变量是结构变量,那么它可以直接用.来取得它的成员。而如果变量是一个结构的指针,那么可以请用 ->来得到它的成员。   顺便说一句,为一个指向结构的指针变量分配的内存,当不再需要时,同样需要记得释放:   //定义指针,并分配内存: BaoBao *pDaBao = new BaoBao;   //为各成员赋值: pDaBao->shenGao = 171; pDaBao->tiZhong = 13.5; strcpy(pDaBao->xingMing,"大宝");   //输出: cout << pDaBao->xingMing << "的身高为: " << pDaBao->shenGao << endl; cout << pDaBao->xingMing << "的体重为: " << pDaBao->tiZhong << endl;   //释放: delete pDaBao;   22.5 结构实例   例子中,我们要求老师输入5个宝宝的数据,然后程序将这5个宝宝的情况打在屏幕上:   打开CB,新建一个控制台工程。   因为需要输入输出,所以先在Uint1.cpp中加下以下黑体部分:   #pragma hdrstop #include <iostream.h>   然后是定义BaoBao结构:   //--------------------------------------------------------------------------- struct BaoBao { ??? char xingMing[11]; ??? int shenGao; ??? float tiZhong; };? //分号结束结构的定义   为了方便演示,我们只让有5个宝宝,在上面的结构定义代码后面加一行:   #define BAOBAO_GESHU 5? //宏定义之后没有分号   然后,我们需定义一个BaoBao数组变量:   int main(int argc, char* argv[]) { ?? BaoBao baoBao[BAOBAO_GESHU]; ?? ??? return 0; }   程序一开始时,我们要求幼儿园老师输入这五个宝宝的数据:   int main(int argc, char* argv[]) { ?? BaoBao baoBao[BAOBAO_GESHU]; ?? ?? for (int i = 0; i < BAOBAO_GESHU; ++i) ?? { ????? cout << "请输入第 " << i + 1 << "个宝宝的数据" << endl;   ????? cout << "姓名:"; ????? cin >> baoBao[i].xingMing;   ????? cout << "身高:"; ????? cin >> baoBao[i].shenGao;   ????? cout << "体重:"; ????? cin >> baoBao[i].tiZhong; ?? }   ?? return 0; }   运行上述程序,我们就可以进行输入了。注意,名字不可以超过5个汉字,否则程序将可能发生不可预料的错误。   最后,再一个循环,我们将5个宝宝的数据输出:   int main(int argc, char* argv[]) { ?? BaoBao baoBao[BAOBAO_GESHU]; ?? ?? for (int i = 0; i < BAOBAO_GESHU; ++i) ?? { ????? cout << "请输入第 " << i + 1 << "个宝宝的数据." << endl;   ????? cout << "姓名:"; ????? cin >> baoBao[i].xingMing;   ????? cout << "身高:"; ????? cin >> baoBao[i].shenGao;   ????? cout << "体重:"; ????? cin >> baoBao[i].tiZhong; ?? }   ?? for (int i = 0; i < BAOBAO_GESHU; ++i) ?? { ????? cout << "第 " << i + 1 << "个宝宝的数据如下" << endl;   ????? cout << "姓名:" << baoBao[i].xingMing << endl;   ????? cout << "身高:" << baoBao[i].shenGao << endl;   ????? cout << "体重:" << baoBao[i].tiZhong << endl; ?? } system("Pause");   ?? return 0; }   运行结果略。   22.6 结构与函数   int foo(int a); 是一个函数,其中a是参数,返回值是一个整数。 假充foo的实现如下:   int foo(int a) { ?? return a * 2; }   那么, 代码:   int b = foo (100); 将使b值为200。 请参看专门函数专门章节。本章中,我们将分别讲如何在参数及返回值中使用结构变量。   22.6.1 结构变量作为函数参数   函数的参数有两种传递方法,一种是传值,一种是传址。如果您觉得有些陌生了,那一定要复习一下函数参数的相关章节。   我们先来定义一个结构,这个结构用于表达一个四边形(SiFangXing):   struct SiFangXing { ??? int l1,l2,w1,w2;? //四方形的四条边 };   现在我们要做两道题:   第一道:写一个函数,对所给的四方形求周长。 第二道:写一个函数,将所给的四方形长宽各增加一倍。   请大家在下面小节中,注意两个函数的参数形递有何不同,并思考各自的目的。   22.6.1.1 结构变量以传值的方式传递   第一道:写一个函数,对所给的四方形求周长。   //求周长: int QiuZhouChang(SiFangXing sfx) { ??? return sfx.l1 + sfx.l2 + sfx.w1 + sfx.w2; //四边之和即是周长 }   22.6.1.2 结构变量以传址的方式传递   第二道:写一个函数,将所给的四方形长宽各增加一倍。   //加倍长宽 void JiaBeiChangKuan(SiFangXing& sfx) { ?? //各边都*2; ?? sfx.l1 *= 2; ?? sfx.l2 *= 2; ?? sfx.w1 *= 2; ?? sfx.w2 *= 2; }   22.6.1.3 结构变量以常量传址方式传递   在22.6.1例中,由于我们并不需要在求周长的函数内改变所传入的四方形,所以我们采用“传值”方式。传值将复制一份实参,然后把“复制品”传给函数。这样就有了一个问题。以前我们写的函数的参数,多是像int,char之类的简单变量,它们占用的内存并不多,复制也快。但今天我们学习结构,结构往往由多个成员变量组成,占用内存较大,如果复制一份,就会显得浪费。并且,占用内存大的结构,在复制起来,比较占用时间。真是时间和空间双重浪费。   怎么办呢?首先想到的是,那就改成传址啊——   int QiuZhouChang(SiFangXing& sfx) { ??? return sfx.l1 + sfx.l2 + sfx.w1 + sfx.w2; //四边之和即是周长 }   采用传址方式后,传递给参数的,其实是实参的地址。而地址的大小只需要4个字节,相当于传一个整形数,又小又快。双重浪费的问题算是解决。但是这却带来了一个“隐患”。如果写QiuZhouChang函数的人一不小心,在该函数内修改了传入的参数,就会造成程序难以查找的错误:   int QiuZhouChang(SiFangXing& sfx) { ?? int zc = sfx.l1 + sfx.l2 + sfx.w1 + sfx.w2; //四边之和即是周长   ?? sfx.l1 *= 2;? //当时写这个函数的程序员发高烧,所以有了这行代码 :)   ?? return zc; }   上面函数显然是错误,对一个四方形求周长的函数,怎么可以莫名地修改人家四方形的边长呢?并且由于参数我们在前面改为用“传址”方式,所以当一个四方形求完周长后,它的边长1竟然长了一倍长……   ... SiFangXing sfxA; //四方形A sfxA.l1 = sfxA.l2 = 10; //长10 sfxA.w1 = sfxA.w2 = 5; ?//宽5   //求周长之前,输出四边长: cout << "四边长:" << sfxA.l1 << "," << sfxA.l2 << "," ?????? << sfxA.w1 << "," << sfxA.w2 << endl;   int zhouChang = QiuZhouChang(sfxA); //求周长   cout << "周? 长:" << zhouChang << endl;   //求周长之后,再输出四边长: cout << "四边长:" << sfxA.l1 << "," << sfxA.l2 << "," ?????? << sfxA.w1 << "," << sfxA.w2 << endl; ...   上面的代码输出结果是:   四边长:10,10,5,5 周? 长:30 四边长:20,10,5,5   你可能会说,我绝不会在发高烧时写代码,但要知道,如果一个函数体内的代码足够复杂,那么每个人都有可能在不发高烧的情况下,也写出愚蠢的代码来。C++提供了一种方法,让我们可更好的避免此类错误代码。这就是我们所说的“常量传址”。   //传址常量形式的参数 int QiuZhouChang (const SiFangXing& sfx) { ?? return sfx.l1 + sfx.l2 + sfx.w1 + sfx.w2; }   1、上述参数形式中,参数 sfx 将以传址的方式来传递。传址方式避免了参数复制品造成的内存与速度的问题。符号“&”标明了它是使用传址。 2、const 修饰符 则指明 sfx 将被当作常量对待,该语法规定你不能在当前函数内修改这个参数——不管你是否发高烧。   请在CB时试着写下面的高烧代码:   int QiuZhouChang(const SiFangXing& sfx) { ?? int zc = sfx.l1 + sfx.l2 + sfx.w1 + sfx.w2; //四边之和即是周长   ?? sfx.l1 *= 2;? //编译时,该行会报错   ?? return zc; }   编译器将拒绝通过上述代码。   趁热打铁,我们再来一个“常量传址”例子,就是上面的输出四方形各边长的代码:   //输出指定四方形的四边长: void ShuChuSiBianChang(const SiFangXing& sfx) { ?? cout << "四边长: " << sfx.l1 << "," << sfx.l2 << "," << sfx.w1 << "," << sfx.w2 << endl; }   像类似上述的两个函数参数,你可以拒绝使用 “&” 来指定使用传址;你也可以拒绝使用 const 来限制它是一个常量。你的代码可以工作,但它们效率不好;并且,你的代码越来越多时,你犯错误的机率很可能急剧地升高,直到整个程序乱成一团。   程序员要养成良好习惯。否则除了上述的问题外,当你与其他具备良好习惯的程序员一起写程序时,你会发现你的代码将无法和别人的代码很好地衔接,甚至不“兼容”。   C++ 和其它语言相比,就是它提供了很多强大的语法功能,但它并不强制你使用。其它的语言,有的是提供了同样的语法功能,并强制你使用;而有些则缺少必要的语法。之所以C++是现在这个样子,有它的历史原因,比如说它必须兼容C语言。   22.6.1.4 兼容C:使用指针传递结构变量   C 当然也只持最普通的“传值”方式:   int QiuZhouChang (SiFangXing sfx) { ?? return sfx.l1 + sfx.l2 + sfx.w1 + sfx.w2; }   但前所言,“传值”有双浪费之弊。我们坚决反倒。微软的程序员也坚决反对——可以 Windows 中很大一部分就是拿C写的。而C既不支持使用“&”来实现传址方式,也不支持 const 修饰为“常量”。怎么办?答案可能很出乎你的意料:没办法。象写操作系统这类程序,“效率”永远是重中之重,所以只要冒着程序有层出不穷的BUG的危险,只考虑如效率了。   在 C 语言时,要实现传址,方法就是用指针。C++ 兼容这一点。   int QiuZhouChang(SiFangXing* psfx) { ?? return psfx->l1 + psfx->l2 + psfx->w1 + psfx->w2;? // . 换成了 ->,因为psf是指针 }   调用时,如果实参不是一个指针,就使用 & 来取址:   ... SiFangXing* sfxA; sfxA.l1 = sfxA.l2 = 10; sfxA.w1 = sfxA.w2 = 5;   int zc = QiuZhouChang(&sfxA); //& 用于得到sfxA地址   传指针就是传地址。效率问题解决了,但“高烧”代码编译器不会指出:   int QiuZhouChang(SiFangXing* psfx) { ?? int zc = psfx->l1 + psfx->l2 + psfx->w1 + psfx->w2;? // . 换成了 ->,因为psf是指针   ?? psfx->l1 *= 2;   ?? return zc; }   编译器会认为,或许这就是你原来想要的。事实上,编译器读不懂英语,更不懂得中国拼音,它,怎么知道你是“QiuZhouChang”函数是要“求周长”呢?:P   22.6.2 函数返回值是结构类型   函数的返回值也可以一个结构变量。   让我们来实现这么一个功能:把四方形A 和四方形B相加,得到四方形C。相加的方法是长+长,宽+宽。   SiFangXing AddSiFangXing (const SiFangXing& aSfx, const SiFangXing& bSfx) { ?? SiFangXing cSfx;   ?? cSfx.l1 = aSfx.l1 + bSfx.l1; ?? cSfx.w1 = aSfx.w1 + bSfx.w1;   ?? cSfx.l2 = aSfx.l2 + bSfx.l2; ?? cSfx.w2 = aSfx.w2 + bSfx.w2;   ?? return cSfx; }   调用样例为:   ... SiFangXing a,b,c;   a.l1 = a.l2 = 10; a.w1 = a.w2 = 5;   b.l1 = b.l2 = 20; b.w1 = b.w2 = 18;   c = AddSiFangXing ( a, b );   ShuChuSiBianChang(c); //输出,见22.6.3节 ...   上这代码结果为: 四边长: 30,30,23,23   22.7 作业   一、本章中的所有例程,请都在CB上演练一番。 二、写一个求四方形面积的函数。 三、请写一个将四方形长和宽对调的函数。 四、请写一个函数,输入两个四方形,返回其中面积较大者。 五、请定义一个“圆”的结构。并写相应的三个函数实现:1、求圆周长,2、求圆面积,3、让指定的圆周长增加一倍。