——理解C++和数据结构的基础
指针不仅可以指向变量、数组、函数,还可以和结构(structure)联系起来,这使得C语言的威力倍增,初学C语言的朋友对结构可能不太重视,对它的理解也不够深入,但事实上,结构是一个非常重要的工具,有了它我们可以很轻松的构建一些仅靠其它C语言特性做起来很复杂的程序。深入地理解结构会对你理解C++的面向对象有很大帮助,并且会让在你学习数据结构时有一份惬意的心情。(本讲中默认读者已经对结构有了基本的认识)
附:在本讲的最后,会提供上一讲复杂声明的答案。
1.深入理解结构
<1>.为什么需要结构
C语言中有很多内置类型,如int型、double型、char型等,但仅仅使用这些类型并不能很好地表达我们的意图。假如我们想表示一辆汽车,汽车有很多自身的属性,如:最大功率、车重、颜色、最高时速、价格等等。这些属性都应该和汽车紧密相连,我们每构造一辆车时,这些固有的属性都应该一并被定义。有了这种需求,就催生了结构。结构有时也被翻译为结构体。当我们定义了一个结构后,就意味着定义了一种新的类型,结构是C语言中为数不多的能让我们自己掌控所定义类型的语言特性,在C中使用结构可以把不同类型的值存储在一起。
<2>.使用结构
首先看一个例子,我将用这个例子贯穿本讲的内容:
struct car
{
char name[50];
int max_power;
int weight;
char color[20];
int max_speed;
int price;
struct car *next;
} one_car, famous_cars[12];
关键字struct表示将要定义一个结构,编译器会将从struct直到分号为止的部分识别为一个结构。花括号中存放我们要进行组合的内容,car是一个可选(optional)的结构标签(tag),也叫结构名,有了这个标签我们就可以在将来的程序中用struct car作为struct{int max_power; int weight; char color[20]; int max_kph; float price;}的简写形式。右花括号后的one_car是一个structcar类型的变量,而famous_cars[12]是一个数组,这个数组包含了20个structcar类型的元素。
结构中的所有内容都称作成员,所有成员必须在结构的内部声明,一旦结构定义完成后,就没有任何办法可以增加成员了。当定义了一个结构后不仅定义了一个新的类型,同时也定义了一个新的作用域,在struct car中有7个成员,这些成员的作用范围只是这个结构中,我们在结构外是看不到也用不了的,如果想用怎么办呢。可以通过成员操作符(.)来访问结构中的成员。(.)的左操作数是结构变量名,右操作数是成员名,如果我想把one_car的颜色设置成红色,那么可以写one_car.color= "red";现在我还想将12辆名车中的第一辆车的价格调整到525万元,如:famous_cars[0].price = 525;此时的famous_cars[0]和one_car是同一类的对象,都是struct car类型的变量,只不过famous_cars[0]是隶属于famous_cars[12]这个数组的一个元素,而one_car是个自由身罢了。
<3>.结构的自引用
大家应该早已注意到struct car中的最后一个成员struct car *next了,有些朋友对此不太理解。结构可以包含任意类型的成员,包括其他类型的结构,当然也可以包含自身类型的结构,但在定义这种结构时,只能写成包含指向自身类型的指针,而不能写成以下这种形式:
struct car
{
char name[50];
int max_power;
int weight;
char color[20];
int max_speed;
int price;
struct car next; //错,此时类型不完整,不能包含自身类型的变量
};
因为next是另一个完整的结构变量,它的内部同样会包含一个成员struct car next,这个成员还会包括一个成员struct car next,如此下去永无休止,编译器将无法确定这个结构struct car的大小。为了解决这个问题,我们可以把第一个成员声明为struct car *next;,一个struct car结构的指针就是用来存储同类型结构变量的地址的,是有固定长度的,此时编译器可以轻松确定struct car的大小。
2.指向结构的指针
刚刚说过的struct car *next就是一个指向结构的指针,它既可以在结构内作为一个结构成员,也可以作为一个自由的对象出现在结构定义后面的任何地方,现在我们再来定义一个指向struct car类型的指针car_pointer:structcar *car_pointer = &one_car;。现在我们拥有了一个指向结构的指针,如何通过这个指针来访问结构变量中的成员呢?同样是通过解引用操作符,比如现在想将one_car的发动机进行调教,把最大功率增加100千瓦:(*car_pointer).max_power += 100;首先通过*操作符获取结构变量one_car,然后再对它的max_power成员进行修改。特别要注意括号不能丢,因为成员操作符.的优先级高于*操作符,如果没有括号的话将会导致错误。正是由于这个有些令人厌烦的访问方式,C语言提供了更加快捷且易于理解的方式——(->)操作符。通过这个操作符,我们就无需再受丢括号的困扰了。 -> 操作符和 . 操作符具有同样的优先级。使用方法:car_pointer-> max_power += 100;
3.链式存储
有了以上的基础,现在可以来讨论通过结构与指向结构的指针构建的一种存储数据的方式——链式存储了。假设现在要组织一次名车的巡礼活动,车辆的排列顺序按品牌的字典序进行。为了保证车队的连贯性,车与车之间通过绳索连接起来。这幅图景如果用程序来表现是这样的:
#include <stdio.h>
#include <stdlib.h>
struct car
{
char name[50];
int max_power;
int weight;
char color[20];
int max_speed;
int price;
struct car *next;
};
int main()
{
int i = 0;
struct car*car_pointer = (struct car *)malloc(sizeof(struct car));
struct car *record = car_pointer;
struct car *remove = car_pointer;
struct car *current = car_pointer;
for (i; i < 2; i++)
{
car_pointer->next = (struct car *)malloc(sizeof(structcar));
printf("Please input car's name : ");
scanf("%s",car_pointer->name);
printf("%s's max_power : ",car_pointer->name);
scanf("%d",&car_pointer->max_power);
printf("%s's weight : ",car_pointer->name);
scanf("%d",&car_pointer->weight);
printf("%s's color : ",car_pointer->name);
scanf("%s",car_pointer->color);
printf("%s's max_speed : ",car_pointer->name);
scanf("%d",&car_pointer->max_speed);
printf("%s's price : ",car_pointer->name);
scanf("%d",&car_pointer->price);
printf("\n");
car_pointer = car_pointer->next;
}
car_pointer->next =NULL;
printf("巡礼车辆如下:\n");
for (record;record->next != NULL; record = record->next)
{
printf("%s\n",record->name);
printf("%s's max_power : %dKW\n", record->name, record->max_power);
printf("%s's weight : %dKG\n", record->name, record->weight);
printf("%s's color : %s\n",record->name, record->color);
printf("%s's max_speed : %dKM/H\n", record->name, record->max_speed);
printf("%s's price : %d万元(¥)\n",record->name, record->price);
printf("\n");
}
while(current->next != NULL)
{
remove = current;
current = current->next;
free(remove);
}
return 0;
}
首先,我们定义一个结构,然后在主函数中开辟一段能容纳结构struct car的内存,并定义一个指向此结构类型的指针car_pointer将其指向刚才所开辟的内存空间。接下来定义几个同类型的指针并初始化为car_pointer留作他用。在链式存储中,每一块这样的内存空间都被称作结点,代表着链中的一个实体。接下来的for循环用来控制创建多少个结点,每一次循环,都创建一个新的结点,并用前一个结点的next成员指向新创建的结点,再通过相应的输入来初始化每个结点的各个成员。在循环结束后,将最后一个结点的next成员置为NULL。
然后,用先前创建的指针record来控制这个车队的输出,因为此时car_pointer已经走到了车队的末尾,无法再用它从头进行遍历了。
最后将动态分配的内存释放掉。注意此时同样需要另一个指向车队头部的指针current,用来遍历车队链表,并创建一个remove指针指向要删除的结点。如果我们不设置remove指针而是直接将current所指的结点释放掉,就找不到下一个结点了。本例使用链式存储结构所创建的车队链表如下图所示:
4.用typedef给结构创建别名
本将的最后一个话题,我们再来谈谈typedef。关键字typedef用来给一种类型起一个别名,理所当然地可以给一个结构类型定义一个别名,用法上没有什么区别。如果想给struct car定义一个别名Car可以这样处理:
typedef struct car
{
char name[50];
int max_power;
int weight;
char color[20];
int max_speed;
int price;
struct car *next;
} Car;
通过typedef引入Car这个名字作为struct car {……}的简写形式。此后我们就可以用Car来定义变量了,如Car one_car;或Car famous_cars[12]; 当然也可以按如下的方式来起别名,而且会更清晰易读:
struct car
{
char name[50];
int max_power;
int weight;
char color[20];
int max_speed;
int price;
struct car *next;
};
typedef struct car Car;
typedef struct car *CarPointer;
此时的CarPointer是struct car *类型的别名,即指向struct car的指针类型。理解了这种表示法对于理解C++中的类是很有帮助的。
差一点儿忘了,在第四讲中复杂声明还有悬而未决的问题,现在给出解答,如果有不明白或者有兴趣深入研究的朋友可以联系我:邮箱:porscheyin@hotmail.com,以下是原题及答案:
1. int *( *( *a[5]) ( ) ) ( );
Answer:a是一个数组,它的5个元素都是指向函数的指针,该函数仍旧返回一个指向函数的指针。
2. void * (*b) ( char, int (*) ( ) );
Answer:b是一个函数指针,该函数接受两个形参,分别是char型和一个函数指针,返回值类型为void *。
3. float ( *(*c[10]) (int *) ) [5];
Answer:c是一个数组,它的10个元素都是指向函数的指针,所指函数接受int *型形参并返回一个指向数组的指针,这个数组包含5个float元素。
4. int ( *(*d)[2][3] ) [4][5];
Answer:d是一个指向数组的指针,此数组的元素又是一个指向数组的指针。
5. int (*(*(*e) ( int* ))[15]) (int*);
Answer:e是一个函数指针,该函数的返回值是一个指向数组的指针,所指向数组的元素又是函数指针,指向的函数具有int *型形参,返回值类型为int。
6. int ( *(*f[4][5][6]) (int*) ) [10];
Answer:f是一个数组,这个数组的元素是函数指针,这类函数具有int *型形参并返回指向数组的指针,所指向的数组的元素是具有10个int型元素。
7. int *(*(*(*g)( ))[10]) ( );
Answer:g是个指向函数的指针,它所指向的函数返回一个指向包含10个元素的数组的指针,数组元素的类型是指向函数的指针,所指向的函数不接受参数且返回值类型为int *。
另外,对于本讲中的巡礼车队程序,如果进行相应输入后,会得到类似下面的输出。我选了一些最新的世界知名车辆,它们的品牌与第四讲中指针数组例子中的车辆品牌一致,我只是在每个品牌中选了一个车系,下面是输出结果:
巡礼车辆如下:
ASTON-MATIN--DB9
ASTON-MATIN--DB9's max_power : 335KW
ASTON-MATIN--DB9's weight : 1760 KG
ASTON-MATIN--DB9's color : GREEN
ASTON-MATIN--DB9's max_speed : 300 KM/H
ASTON-MATIN--DB9's price : 304万元(¥)
AUDI--R8
AUDI--R8's max_power : 309 KW
AUDI--R8's weight : 1560 KG
AUDI--R8's color : SILVER
AUDI--R8's max_speed : 301 KM/H
AUDI--R8's price : 180万元(¥)
BENZ--SLR-722
BENZ--SLR-722's max_power : 478 KW
BENZ--SLR-722's weight : 1550 KG
BENZ--SLR-722's color : BLACK
BENZ--SLR-722's max_speed : 337 KM/H
BENZ--SLR-722's price : 888万元(¥)
BENTLEY--CONTINENTAL-GTC
BENTLEY--CONTINENTAL-GTC'smax_power : 411 KW
BENTLEY--CONTINENTAL-GTC's weight: 2495 KG
BENTLEY--CONTINENTAL-GTC's color :AZURE
BENTLEY--CONTINENTAL-GTC'smax_speed : 312 KM/H
BENTLEY--CONTINENTAL-GTC's price :358万元(¥)
BMW--M6
BMW--M6's max_power : 373 KW
BMW--M6's weight : 1785 KG
BMW--M6's color : BLUE
BMW--M6's max_speed : 250 KM/H
BMW--M6's price : 229万元(¥)
BUGATTI--VEYRON-16.4
BUGATTI--VEYRON-16.4's max_power :736 KW
BUGATTI--VEYRON-16.4's weight : 1888 KG
BUGATTI--VEYRON-16.4's color :RED&BLACK
BUGATTI--VEYRON-16.4's max_speed :407 KM/H
BUGATTI--VEYRON-16.4's price :2500万元(¥)
FERRARI--599GTB
FERRARI--599GTB's max_power : 456KW
FERRARI--599GTB's weight : 1690 KG
FERRARI--599GTB's color : RED
FERRARI--599GTB's max_speed : 330 KM/H
FERRARI--599GTB's price : 376万元(¥)
JAGUAR--XK
JAGUAR--XK's max_power : 224 KW
JAGUAR--XK's weight : 1595 KG
JAGUAR--XK's color : GREEN
JAGUAR--XK's max_speed : 250 KM/H
JAGUAR--XK's price : 142万元(¥)
LAMBORGHINI--MURCIELAGO-LP640
LAMBORGHINI--MURCIELAGO-LP640'smax_power : 471 KW
LAMBORGHINI--MURCIELAGO-LP640'sweight : 1665 KG
LAMBORGHINI--MURCIELAGO-LP640'scolor : BLACK
LAMBORGHINI--MURCIELAGO-LP640'smax_speed : 340 KM/H
LAMBORGHINI--MURCIELAGO-LP640'sprice : 438万元(¥)
MASERATI--GRANTURISMO
MASERATI--GRANTURISMO's max_power: 298 KW
MASERATI--GRANTURISMO's weight : 1955 KG
MASERATI--GRANTURISMO's color :RED
MASERATI--GRANTURISMO's max_speed: 285 KM/H
MASERATI--GRANTURISMO's price :219万元(¥)
MAYBACH--62S
MAYBACH--62S's max_power : 450 KW
MAYBACH--62S's weight : 2855 KG
MAYBACH--62S's color : WHITE
MAYBACH--62S's max_speed : 250 KM/H
MAYBACH--62S's price : 900万元(¥)
ROLLS-ROYCE--PHANTOM-101EX
ROLLS-ROYCE--PHANTOM-101EX'smax_power : 338 KW
ROLLS-ROYCE--PHANTOM-101EX'sweight : 2590 KG
ROLLS-ROYCE--PHANTOM-101EX's color: BLUE
ROLLS-ROYCE--PHANTOM-101EX'smax_speed : 250 KM/H
ROLLS-ROYCE--PHANTOM-101EX's price: 680万元(¥)