论结构体

结构体是一个典型的从工程中诞生的封装结构,是需求选择了结构体,而不是结构体创造的需求。


1. 结构体是什么

维基百科中结构体的介绍如下:

在C语言中,结构体(struct)指的是一种数据结构,是C语言中复合数据类型(aggregate data type)的一类。

用通俗的语言概括就是把基本的数据类型按需组合,所构成的复合的数据类型。

2. 结构体有什么用

C语言诞生之初,并没有struct结构体这玩意,遇到了多种数据类型耦合起来的复合数据类型,只能开多个类型的数组,并且设法进行管理。无疑既不优雅,又不便于理解,于是结构体诞生了。

工程师们喜欢层层封装,让代码抽象得具体,或者说具体的抽象,而结构体正是最典型的封装之一。对于一个大的工程而言,基本的数据类型无疑是不充分的,无法适应所有需求;而结构体的出现,以其灵活和强大瞬间给了这些问题一个解决方案。

我们可以用结构体组合多种变量去描述复杂的物体;我们可以把一组有关联的数据打包起来,一起传入函数或者作为函数的返回值,而不用零散地处理数据。

譬如你为商场超市编写一个商品管理程序,相比于写一堆数组去储存数据,如下的结构体显然更简洁:

1
2
3
4
5
6
struct goods  //商品
{
    char name[20];  //商品名
    float price;  //单价
    float weight;  //重量
};

C语言最初是作为UNIX操作系统的工具而存在的。在UNIX系统中,UNIX v3开发出了具有struct功能的编译器,在随后的版本里立刻就开始使用。

可见结构体的诞生绝不是偶然,而是特定条件下的必然。其本质就是大型工程中,由程序员们对抽象封装有着强烈需求,进而选择创造了结构体。是需求选择了结构体,而不是结构体创造的需求。

3. 使用结构体的注意事项

  • 在C语言和C++中,结构体不完全一样,以下举例说明。

    • C语言中定义和声明结构体:
    1
    2
    3
    4
    5
    6
    7
    8
    
    struct goods  //商品
    {
        char name[20];  //商品名
        float price;  //单价
        float weight;  //重量
    };
    
    struct goods var_goods;  //struct goods是类型名,var_goods是变量名
    

    或者使用typedef关键字定义一个类型别名:

    1
    2
    3
    4
    5
    6
    7
    8
    
    typedef struct goods  //商品
    {
      char name[20];  //商品名
      float price;  //单价
      float weight;  //重量
    }Good;
    
    Good var_goods;  //“Good”是类型别名,var_goods是变量名,“Good”的使用和“int”类似
    
    • C++中定义和声明结构体:
    1
    2
    3
    4
    5
    6
    7
    8
    
    struct goods  //商品
    {
        char name[20];  //商品名
        float price;  //单价
        float weight;  //重量
    };
    
    goods var_goods;  //goods是类型名,var_goods是变量名
    

    C++中structclass本质都是类,所以使用类似,不需要加typedef

  • 结构体中->.的异同 这里引用知乎——Lion Yang的概括:

    简单的说,就是一个快捷方式,一个语法糖。

    照例用代码举例:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    #include <iostream>
    using namespace std;
    
    struct goods  //商品
    {
        char name[20];  //商品名
        float price;  //单价
        float weight;  //重量
    };
    
    int main(void) {
        goods s;  //结构体
        goods* p;  //结构体指针
        p = new goods;
        s.price = 10;
        p->price = 10;
        if ((&s)->price == (*p).price) {
            cout << "价格相等!" << endl;
        }
        delete p;
    }
    

    可以看到我们用了s.pricep->price进行赋值,也可以用(&s)->price(*p).price进行读取。其本质并无太多差别,在内存中都是从结构体的基址进行偏移,在偏移后的内存地址赋值或取得所需数据。不过在使用上有些许区别:->的左操作数是一个结构体指针变量.的左操作数是一个结构体变量

    所以上面程序的结果是:

    1
    
    价格相等!
    

    这两个操作符是历史遗留,当时编程大多是从机器的角度出发,编写的代码贴近机器逻辑而反人类直觉。假如机器会思考,(*p).i对它而言就像在数轴上从p开始数i个字;而对于p.i它需要先找到p的位置再开始数。机器终究是为人类服务的,所以为了更直观地编程加入了->操作符,使用->可以让结构体指针(*p).i简单地表示为p->i,成为了折中的方案。

引用