结构中的结构

别忘了,定义结构其实就是在创造新的数据类型。C语言有很多内置数据类型,例如intshort,但有了结构,我们就可以把现有类型组合起来,向计算机描述更复杂的东西。

既然结构可以用现有数据类型创建数据类型,也就能用其他结构创建结构。具体怎么做?来看一个例子。

结构中的结构 - 图1

上述代码向计算机描述了一个结构中的结构。你可以像之前一样用数组语法创建变量,但现在可以在数据中包含结构中的结构。

结构中的结构 - 图2

一旦把结构组合起来,就可以使用一连串的“.”运算符来访问字段:

  1. printf("Snappy 喜欢吃 %s", snappy.care.food);
  2. printf("Snappy 喜欢锻炼 %f hours", snappy.care.exercise_hours);

快试试你的新技能吧……

为什么要嵌套定义结构?

之所以要这么做是为了对抗复杂性。通过使用结构,我们可以建立更大的数据块。通过把结构组合在一起,我们可以创建更大的数据结构。本来你只能用intshort,但有了结构以后,就可以描述十分复杂的东西,比如网络流和视频图像。

 

结构中的结构 - 图3练习

Head First水族馆的人开始登记鱼的信息,每条鱼都有很多数据要记录,这是它们的结构:

  1. struct exercise {
  2. const char description;
  3. float duration;
  4. };
  5.  
  6. struct meal {
  7. const char ingredients;
  8. float weight;
  9. };
  10.  
  11. struct preferences {
  12. struct meal food;
  13. struct exercise exercise;
  14. };
  15.  
  16. struct fish {
  17. const char name;
  18. const char species;
  19. int teeth;
  20. int age;
  21. struct preferences care;
  22. };

其中某条鱼有以下数据要记录:

  1. Name: Snappy
  2. Species: Piranha
  3. Food ingredients: meat
  4. Food weight: 0.2 lbs
  5. Exercise description: swim in the jacuzzi
  6. Exercise duration 7.5 hours

问题0:这条数据用C语言怎么表示?

结构中的结构 - 图4

问题1:补全label()函数的代码,输出以下信息:

结构中的结构 - 图5

 

结构中的结构 - 图6练习解答

Head First水族馆的人开始登记鱼的信息,每条鱼都有很多数据要记录,这是它们的结构:

  1. struct exercise {
  2. const char description;
  3. float duration;
  4. };
  5.  
  6. struct meal {
  7. const char ingredients;
  8. float weight;
  9. };
  10.  
  11. struct preferences {
  12. struct meal food;
  13. struct exercise exercise;
  14. };
  15.  
  16. struct fish {
  17. const char name;
  18. const char species;
  19. int teeth;
  20. int age;
  21. struct preferences care;
  22. };

其中某条鱼有以下数据要记录:

  1. Name: Snappy
  2. Species: Piranha
  3. Food ingredients: meat
  4. Food weight: 0.2 lbs
  5. Exercise description: swim in the jacuzzi
  6. Exercise duration 7.5 hours

问题0:这条数据用C语言怎么表示?

结构中的结构 - 图7

问题1:补全label()函数的代码,输出以下信息:

结构中的结构 - 图8

结构中的结构 - 图9

用typedef为结构命名。

当创建内置数据类型变量时,只要写intdouble就行了,但每次创建结构变量时,不得不加上struct关键字。

  1. struct cell_phone {
  2. int cell_no;
  3. const char *wallpaper;
  4. float minutes_of_charge;
  5. };
  6. ...
  7. struct cell_phone p = {5557879, "sinatra.png", 1.35};

在C语言中可以为结构创建别名,你只要在struct关键字前加上typedef,并在右花括号后写上类型名,就可以在任何地方使用这种新类型。

结构中的结构 - 图10

typedef可以用来缩短代码长度,并让代码更容易阅读。试试在代码中加入typedef……

新类型叫什么?

当你用typedef为结构创建别名,需要决定别名叫什么。别名其实就是类型名,也就是说结构有两个名字:一个是结构名(struct cell_phone),另一个是类型名(phone)。为什么要有两个名字?一般一个就够了。如果只写类型名而不写结构名,编译器也没意见:

结构中的结构 - 图11

 

结构中的结构 - 图12练习

潜水员要开始巡逻水池,他需要给潜水服贴上新的标签。问题是一些代码不见了,你能写出来吗?

结构中的结构 - 图13

 

结构中的结构 - 图14练习解答

潜水员要开始巡逻水池,他需要给潜水服贴上新的标签。问题是一些代码不见了,你写出来了吗?

结构中的结构 - 图15

 

结构中的结构 - 图16要点

  • 结构是一种由一系列其他数据类型组成的数据类型。

  • 结构的大小固定。

  • 结构字段按名访问,用<结构>.<字段名>语法(也叫“点表示法”)。

  • 结构字段在存储器中保存的顺序和它们出现在代码中的顺序相同。

  • 可以嵌套定义结构。

  • typedef创建数据类型的别名。

  • typedef定义结构时可以省略结构名。

 

这里没有蠢问题

问:结构字段在存储器中是紧挨着摆放的吗?

:有时两个字段之间会有小的空隙。

问:为什么?

:计算机总是希望数据能对齐字边界(word boundary)。如果计算机的字长是32位,就不希望某个变量(比如short)跨越32位的边界保存。

问:所以计算机会留下一道空隙,然后在下一个32位字开始的地方保存short

:是的。

问:也就是说,每个字段都占用一整个字?

:不一定,计算机在两个字段之间留出空隙仅仅是为了防止某个字段跨越字边界。如果几个字段能放在一个字中,计算机就会那么做。

问:为什么计算机如此在意字边界?

:计算机按字从存储器中读取数据,如果某个字段跨越了多个字,CPU就必须读取多个存储器单元,并以某种方式把读到的值合并起来。

问:这样会很慢吗?

:会很慢。

问:在Java那样的语言中,如果我把对象赋给变量,它不会复制对象,仅仅复制引用,为什么C语言不这样做?

:在C语言中,所有赋值都会复制数据,如果你想复制数据的引用,就应该赋指针。

问:结构名的问题我还没搞清楚,什么是结构名?什么是别名?

:结构名是struct关键字后 面的那个单词。假设你写的是struct peter_parker{ … },那么结构名就是peter_parker,当创建变量时,你会写struct peter_parker x

问:那别名呢?

:有时,你不想在声明变量时还使用struct关键字,那么就可以用typedef创建别名。typedef struct peter_parker { … } spider_man;里面的spider_man就是别名。

问:什么是匿名结构?

:匿名结构就是没有名字的结构,typedef struct { … } spider_man;有一个叫spider_man的别名,但没有结构名。很多时候,如果创建了别名,也就不需要结构名了。