五.指针与结构

转自:http://blog.csdn.net/porscheyin/article/details/3461648(有稍修改,还请参考原处…)

  ——理解C++和数据结构的基础

指针不仅可以指向变量、数组、函数,还可以和结构(structure)联系起来,这使得C语言的威力倍增,初学C语言的朋友对结构可能不太重视,对它的理解也不够深入,但事实上,结构是一个非常重要的工具,有了它我们可以很轻松的构建一些仅靠其它C语言特性做起来很复杂的程序。深入地理解结构会对你理解C++的面向对象有很大帮助,并且会让在你学习数据结构时有一份惬意的心情。(本讲中默认读者已经对结构有了基本的认识)

附:在本讲的最后,会提供上一讲复杂声明的答案。

1.深入理解结构

<1>.为什么需要结构

C语言中有很多内置类型,如int型、double型、char型等,但仅仅使用这些类型并不能很好地表达我们的意图。假如我们想表示一辆汽车,汽车有很多自身的属性,如:最大功率、车重、颜色、最高时速、价格等等。这些属性都应该和汽车紧密相连,我们每构造一辆车时,这些固有的属性都应该一并被定义。有了这种需求,就催生了结构。结构有时也被翻译为结构体。当我们定义了一个结构后,就意味着定义了一种新的类型,结构是C语言中为数不多的能让我们自己掌控所定义类型的语言特性,在C中使用结构可以把不同类型的值存储在一起。

<2>.使用结构 ------ 末尾一定要记得加封号' ; ' 
                          ------同时成员不能有本结构类型的变量,顶多只能包含本结构类型的指针 
                          ------用结构体变量.成员 and结构体指针->成员 and(*结构体指针).成员 的形式访问成员

首先看一个例子,我将用这个例子贯穿本讲的内容:

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的大小,而这时肯定不允许出现的嘛

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:                   struct car *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++)

   {

       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->next = (struct car *)malloc(sizeof(structcar));

       car_pointer = car_pointer->next;

   }

   car_pointer->next =NULL;//可这样的算法会导致最后一个已分配了空间的结构体却是为空,也就是最后一个有实际存储内容的结构体的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);

   }//这样会导致最后一个没存储内容但已分配了空间的结构体没被free ,造成内存泄露

   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++中的类是很有帮助的。



前言

1.指针到底是什么

2.指针的定义及运算

3.指针与数组的“爱恨情仇”

4.分清函数指针和指针函数

6.使用指针时的“陷阱”

后记

======================================================================================

末尾一定要记得加封号' ; ' 
一定不能有本结构类型变量的成员,只能包含本结构类型的指针 
结构体变量.成员 and结构体指针->成员 and(*结构体指针).成员 的形式访问成员

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值