张银峰的编程课堂

引入结构体

结构体这一语言特性的引入,是为了便于我们更好的抽象问题。考虑一下用C来描述一只猫的特性,比如名字,年龄,体重,花色...

char  cat_name[64];
int   cat_color;
int   cat_age;
float cat_weight;

其实我有一个秘密要告诉你,我有两只猫,不假思索,我们可以这么描述另一只猫的特性。

char  cat_name2[64];
int   cat_color2;
int   cat_age2;
float cat_weight2;

显然,当有更多的小家伙加入队伍时,这并不是好的方法,数组或许可以充当一个奇兵。

char  cat_name[2][64];
int   cat_color[2];
int   cat_age[2];
float cat_weight[2];

cat_name是一个二维数组,现在仅需要知道它可以保存两只猫的名字,每个名字最多64个字符即可(含字符串结束符)。现在,我们可以优雅的使用循环来输出猫的信息了。

for (int i = 0; i < 2; i++)
{
    printf("%s: %d, %d, %.2f\n", cat_name[i], cat_color[i], cat_age[i], cat_weight[i]);
}

将输出提取成一个独立的函数,可以便于我们在多个地方查看猫的特性。

void print_cat(const char *name, int color, int age, float weight)
{
    printf("%s: %d, %d, %.2f\n", name, color, age, weight);
}

ZYF

如果我们还要记录猫的品种、生日等更多特性,这将会导致更多分散的变量定义,更加冗长的函数参数列表等...,所以先停下来分析一下:

  • 所有变量都与猫相关
  • 所有变量是分散的

这就是说:这种属于同一对象的特性,却各自分离!如果C语言能内置一个满足我们需要的Cat类型,那事情会好得多,显然这不现实。好消息是,C语言提供了结构体特性,允许我们将多个属性组织成一个整体,并产生一个类型,这种内部结构由我们自己定义的类型,也称为自定义数据类型;新定义的类型,可以看作是我们对现有事物的抽象。定义一个结构的语法如下:

struct 类型名
{
    成员表列
};

现在,可以这样用struct来定义(抽象)出猫这种数据类型。

struct Cat
{
    char  name[64];
    int   color;
    int   age;
    float weight;
};

注意Cat类型中成员的命名,如name,而非cat_name。我们去掉了前缀cat,是因为这个name特性已经属于Cat类型了,所以没有必要再次指定了。显然,结构体的优异特性,就是允许我们把一个事物相关的特性集中在一起,更重要的是引入了一种类型,同内置类型相似,我们可以定义这种类型的变量,将其传递到函数、从函数返回等;不同的是,关键字struct是类型定义的一部分。如定义Cat类型变量cat1、cat2

struct Cat cat1;
struct Cat cat2;

定义Cat类型的数组

struct Cat cats[2];

对于函数声明或定义的参数,struct关键字也不可缺失。

void print_cat(struct Cat my_cat)
{
    printf( "meow...\n" );
}

可以看到,结构体给程序设计带来本质性变化:将数据组织起来,抽象出新的类型!这种变化使得我们可以建立更好的逻辑。

ZYF