Skip to content

1.结构体

1.1 结构体的格式

结构体和数组一样属于构造类型,由基本类型或者指针类型按一定规律组合而成。数组是用于保存一组相同类型数据的,而结构体是用于保存一组不同类型数据的。

结构体的格式:

cpp
struct 结构体名
{
    类型名1  成员名1;
    类型名2  成员名2;
    ......
    类型名n  成员名n;
    
    函数1[]  
    函数2[]  
    ...
    函数n[]  
};

在C语言中,结构体内不允许包含函数,但是在C++中允许。

1.2 结构体基础

1.2.1 结构体的定义和初始化

定义结构体变量的方式:

  1. 先声明结构体类型再定义变量名。(常用)
    C++中定义结构体变量可以省略 struct 关键字,但是C语言不可以
cpp
#include<iostream>
//示例:
struct Student
{
    char name[10]; // 姓名
    int age; // 年龄
    float height; // 身高

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
};

int main()
{
    struct Student s1 = { "ab", 18, 180.1f };
    Student s11 = {"cd", 19, 180.2f };
    return 0;
}
  1. 在声明类型的同时定义变量。
cpp
#include<iostream>
int main()
{
    struct Student
    {
        char name[10]; // 姓名
        int age; // 年龄
        float height; // 身高

        void print()
        {
            std::cout << "name = " << name << ", age = " << age << ", height = "
            << height << std::endl;
        }
    } s2 = { "cd", 22, 175.2f };

    Student s21 = { "as", 23, 175.3f };

    return 0;
}
  1. 直接定义结构体类型变量。
    第三种方法与第二种方法的区别在于,第三种方法中省去了结构体类型名称,而直接给出结构变量。这种结构体最大的问题是结构体类型不能复用
cpp
#include<iostream>
int main()
{
    struct
    {
        char name[10]; // 姓名
        int age; // 年龄
        float height; // 身高

        void print()
        {
            std::cout << "name = " << name << ", age = " << age << ", height = "
            << height << std::endl;
        }
    } s3 = { "ef", 25, 160.8f };//要定义新的变量必须重新写结构体

    //struct s31 = { "qw", 1, 11.1f };//error

    struct
    {
        char name[10]; // 姓名
        int age; // 年龄
        float height; // 身高

        void print()
        {
            std::cout << "name = "<< name << ", age = "<< age << ", height = "<< height << std::endl;
        }
    } s32 = { "as", 20, 160.9f };//要定义新的变量必须重新写结构体
    return 0;
}

1.2.2 结构体成员的使用

一般对结构体变量的操作是以成员为单位进行的,引用的一般形式为:结构体变量名.成员名

cpp
#include<iostream>
//示例:
struct Student
{
    char name[10]; // 姓名
    int age; // 年龄
    float height; // 身高

    void print()
    {
        std::cout << "name = "<< name << ", age = "<< age << ", height = "<< height << std::endl;
    }
};

int main()
{
    struct Student s1 = { "ab", 18, 180.1f };
    s1.age = 20;
    s1.height = 1.1f;
    s1.print();
    return 0;
}

1.2.3 typedef

每次定义结构体变量都要使用 struct Student,例如:

cpp
struct Student s1 = { "ab", 18, 180.1f };

struct Student 的名称太长,我们可以使用typedef关键字为这些类型起一个别名,别名可以用来代替原有的类型名称。

在C++中,typedef 是一个关键字,用于为已有的数据类型创建别名。它提供了一种方法来简化复杂的类型声明或为类型定义更具描述性的名称。使用typedef可以增加代码的可读性和可维护性。

cpp
//示例:
typedef struct Student
{
    char name[10]; // 姓名
    int age; // 年龄
    float height; // 身高

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

举例如下:STU成了struct Student的别名(只能为已存在的类型起别名)

cpp
#include<iostream>
typedef double DOU;
typedef std::string string;
//typedef std::cout cout;//error

int main()
{
    double d1 = 1.1;
    DOU d2 = 1.2;
    string s = "abc";
    return 0;
}

typedef为C++的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。也可以为基本类型起别名。

cpp
#include<iostream>
typedef int INT;
typedef float FLOAT;

typedef struct Student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

int main()
{
    STU s1 = { "ab", 18, 180.1f };
    s1.print();
    return 0;
}
  • 为函数指针定义别名
    在这个例子中,FunctionPointer 成为了指向参数为两个 int 且返回类型为 int 的函数的指针的别名。
cpp
#include<iostream>
typedef int(*FunctionPointer)(int, int);

int add(int a, int b)
{
    return a + b;
}

int main()
{
    FunctionPointer fp = add;
    int c = fp(1, 2);
    std::cout << "c = " << c << std::endl;
    return 0;
}

1.2.4 结构体数组

当要存储多个结构体变量的时候

cpp
STU stu1 = {"ab", 18, 1.81};
STU stu2 = {"cd", 19, 1.82};
STU stu3 = {"ef", 20, 1.83};

也可以定义为数组形式
结构体数组和普通数组并无太大差异,只不过是数组中的元素都是结构体而已

  • 格式: struct 结构体类型名词 数组名称[元素个数]
cpp
typedef struct Student
{
    char name[10]; // 姓名
    int age; // 年龄
    float height; // 身高

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;
STU stu[3];
  • 初始化,各个结构体变量用 {} 包裹起来,结构体变量间用 , 隔开
cpp
#include<iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

typedef struct Student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

int main()
{
    STU stu[3] = { {"ab", 18, 1.81},
                   {"cd", 19, 1.82},
                   {"ef", 20, 1.83} };
    stu[0].age = 11;
    //stu[1].name = "qq";//"qq"是字符串常量,不能直接赋值

    strcpy(stu[1].name, "qq");
    for (int i = 0; i < 3; i++)
    {
        stu[i].print();
    }
    return 0;
}

1.2.5 结构体嵌套

结构体成员也可以是一个结构体,即构成了嵌套的结构。
对嵌套结构体成员的访问,如果某个成员也是结构体变量,可以连续使用成员运算符"."访问最低一级成员

cpp
#include<iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

struct Date
{
    int year;
    int month;
    int day;
};

typedef struct Student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高
    struct Date birthday;

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

int main()
{
    STU stu[3] = { {"ab", 18, 1.81, {2000, 1, 1}},
                   {"cd", 19, 1.82},
                   {"ef", 20, 1.83} };
    stu[0].age = 11;
    //stu[1].name = "qq"; //"qq"是字符串常量,不能直接赋值

    strcpy(stu[1].name, "qq");
    for (int i = 0; i < 3; i++)
    {
        stu[i].print();
        std::cout << "生日是" << stu[i].birthday.year << "年, "
                  << stu[i].birthday.month << "月, "
                  << stu[i].birthday.day << "日" << std::endl;
    }
    return 0;
}

结构体不可以嵌套自己变量,可以嵌套指向自己这种类型的指针(因为指针的大小是确定的,不管什么类型的指针大小都是8,因此编译器可以为其分配内存,而如果嵌套自身类型的变量,结构体是没法确定大小的)。

cpp
struct Student
{
    char name[10]; // 姓名
    int age; // 年龄
    float height; // 身高
    struct Date birthday;
    struct Student stu; //stu大小不确定,因此编译不通过
} STUDENT; //error

struct Student
{
    char name[10]; // 姓名
    int age; // 年龄
    float height; // 身高
    struct Date birthday;
    struct Student *stu; //*stu大小确定为8,因此编译通过
};

1.2.6 结构体赋值

可以用一个已经存在的同类型的结构体给另一个结构体赋值。

cpp
#include<iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

struct Date
{
    int year;
    int month;
    int day;
};

typedef struct Student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高
    struct Date birthday;

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

int main()
{
    STU stu[3] = { {"ab", 18, 1.81, {2000, 1, 1}},
                   {"cd", 19, 1.82},
                   {"ef", 20, 1.83} };
    stu[0].age = 11;
    //stu[1].name = "qq"; //"qq"是字符串常量,不能直接赋值

    strcpy(stu[1].name, "qq");
    for (int i = 0; i < 3; i++)
    {
        stu[i].print();
        std::cout << "生日是" << stu[i].birthday.year << "年_"
                  << stu[i].birthday.month << "月_" 
                  << stu[i].birthday.day << "日" << std::endl;
    }

    STU stu1 = stu[1];
    stu1.print();
    std::cout << "生日是" << stu1.birthday.year << "年_" 
              << stu1.birthday.month << "月_" 
              << stu1.birthday.day << "日" << std::endl;
    return 0;
}

1.3 结构体和指针

  • 当一个指针变量用来指向一个结构体变量时,称之为结构体指针变量
  • 格式: struct 结构体名 *结构体指针变量名

1.3.1 指向普通结构体变量的指针

和普通指针用法一样, 只是在访问成员时使用->符号

cpp
#include<iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

struct Date
{
    int year;
    int month;
    int day;
};

typedef struct Student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高
    struct Date birthday;

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

int main()
{
    STU stu[3] = { {"ab", 18, 1.81, {2000, 1, 1}},
                   {"cd", 19, 1.82},
                   {"ef", 20, 1.83} };

    stu[0].age = 11;
    //stu[1].name = "qq";//"qq"是字符串常量,不能直接赋值
    strcpy(stu[1].name, "qq");
    for (int i = 0; i < 3; i++)
    {
        stu[i].print();
        std::cout << "生日是" << stu[i].birthday.year << "#, " 
                  << stu[i].birthday.month << "/月," 
                  << stu[i].birthday.day << "日" << std::endl;
    }

    STU stu1 = stu[1];
    stu1.print();
    std::cout << "生日是" << stu1.birthday.year << "#, " 
              << stu1.birthday.month << "/月," 
              << stu1.birthday.day << "日" << std::endl;

    STU* p_stu1 = &stu1;
    (*p_stu1).print();
    std::cout << "生日是" << (*p_stu1).birthday.year << "#, " 
              << (*p_stu1).birthday.month << "/月," 
              << (*p_stu1).birthday.day << "日" << std::endl;

    STU* p = new STU;
    if (p == NULL)
    {
        std::cout << "申请空间失败" << std::endl;
        return -1;
    }

    //将申请到空间清0
    memset(p, 0, sizeof(STU));
    strcpy(p->name, stu1.name);
    //p->name[10] = stu1.name[10];
    p->age = stu1.age;
    p->height = stu1.height;
    p->birthday.year = stu1.birthday.year;
    p->birthday.month = stu1.birthday.month;
    p->birthday.day = stu1.birthday.day;

    p->print();
    std::cout << "生日是" << p->birthday.year << "#, " 
              << p->birthday.month << "/月," 
              << p->birthday.day << "日" << std::endl;
    return 0;
}

1.3.2 堆区结构体

使用malloc/new申请内存,使用完毕后需free/delete(建议使用new和delete)
使用指针访问结构体中的成员时,除了使用(*p_stu1).name先解引用,再通过变量使用.的方法访问外,还可以直接通过指针->进行访问。

cpp
#include<iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

struct Date
{
    int year;
    int month;
    int day;
};

typedef struct Student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高
    struct Date birthday;

    void print()
    {
        std::cout << "name = " << name << "age = " << age << ", height = " << height << std::endl;
    }
} STU;

int main()
{
    STU stu[3] = { {"ab", 18, 1.81, {2000, 1, 1}},
                   {"cd", 19, 1.82},
                   {"ef", 20, 1.83} };

    stu[0].age = 11;
    //stu[1].name = "qq"; //"qq"是字符串常量,不能直接赋值

    strcpy(stu[1].name, "qq");
    for (int i = 0; i < 3; i++)
    {
        stu[i].print();
        std::cout << "生日是" << stu[i].birthday.year << "年, " 
                  << stu[i].birthday.month << "月, " 
                  << stu[i].birthday.day << "日" << std::endl;
    }
    STU stu1 = stu[1];
    stu1.print();
    std::cout << "生日是" << stu1.birthday.year << "年, " 
              << stu1.birthday.month << "月, " 
              << stu1.birthday.day << "日" << std::endl;

    STU* p_stu1 = &stu1;
    (*p_stu1).print();
    std::cout << "生日是" << (*p_stu1).birthday.year << "年, " 
              << (*p_stu1).birthday.month << "月, " 
              << (*p_stu1).birthday.day << "日" << std::endl;

    STU* p = new STU;
    if (p == NULL)
    {
        std::cout << "申请空间失败" << std::endl;
        return -1;
    }

    //将申请到空间清0
    memset(p, 0, sizeof(STU));
    strcpy(p->name, stu1.name);
    //p->name[10] = stu1.name[10];
    p->age = stu1.age;
    p->height = stu1.height;
    p->birthday.year = stu1.birthday.year;
    p->birthday.month = stu1.birthday.month;
    p->birthday.day = stu1.birthday.day;

    p->print();
    std::cout << "生日是" << p->birthday.year << "年, " 
              << p->birthday.month << "月, " 
              << p->birthday.day << "日" << std::endl;

    delete p;//释放申请的空间
    p = NULL;//将指针变量置为NULL

    return 0;
}

1.3.3 结构体中的指针

结构体内含有指针成员。
堆区结构体变量要先为结构体申请空间,然后为结构体里的指针申请空间;
释放的时候先释放结构体里的指针申请的空间,然后释放为结构体申请的空间。

cpp
#include<iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

typedef struct Student
{
    //char name[10]; // 姓名
    char* name;
    INT age; // 年龄
    FLOAT height; // 身高

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

int main()
{
    //要先定义字符数组,然后用字符数组初始化字符指针
    char name[] = "abc";
    //STU stu = {"abc", 18, 181.1f};//error abc是字符串常量,不能为指针赋值
    //STU stu = { NULL, 18, 181.1f }//error
    STU stu = { name, 18, 181.1f };
    stu.print();
    
    //堆区结构体变量首先为结构体申请空间
    STU* stu1 = new STU;
    //然后为指针变量申请空间
    stu1->name = new char[10];
    strcpy(stu1->name, "qwe");
    stu1->age = 18;
    stu1->height = 180.1f;
    stu1->print();
    
    //释放顺序:先释放内部指针,再释放结构体指针
    delete stu1->name;
    stu1->name = NULL;
    delete stu1;
    stu1 = NULL;
    return 0;
}

1.4 结构体和函数

结构体虽然是构造类型, 但是结构体之间赋值是值拷贝, 而不是地址传递。

1.4.1 结构体普通变量做函数参数

结构体变量作为函数形参时也是值传递, 在函数内修改形参, 不会影响外界实参

cpp
#include<iostream>
#include<cstring>
typedef int INT;
typedef float FLOAT;

typedef struct Student
{
    char name[10]; // 姓名
    //char* name;
    INT age; // 年龄
    FLOAT height; // 身高
    
    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

void setvalue(STU stu)
{
    strcpy(stu.name, "qwe");
    stu.age = 20;
    stu.height = 181.2f;
}

int main()
{
    STU stu = { "abc", 18, 180.1f };
    setvalue(stu);
    stu.print();
    return 0;
}

1.4.2 结构体指针变量做函数参数

地址传递

cpp
#include<iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

typedef struct Student
{
    char name[10]; // 姓名
    //char* name;
    INT age; // 年龄
    FLOAT height; // 身高

    void print()
    {
        std::cout << "name = " << name << ", age = " << age << ", height = " << height << std::endl;
    }
} STU;

void setvalue(STU* stu)
{
    strcpy(stu->name, "qwe");
    stu->age = 20;
    stu->height = 181.2f;
}

int main()
{
    STU stu = { "abc", 18, 180.1f };
    setvalue(&stu);
    stu.print();
    return 0;
}

1.4.3 结构体数组名做函数参数

地址传递

cpp
#include<stdio.h>

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

void set_stu_pro(struct stu *tmp, int n)
{
    int i = 0;
    for (i = 0; i < n; i++)
    {
        sprintf(tmp->name, "name%d%d%d", i, i, i);
        tmp->age = 20 + i;
        tmp++;
    }
}

int main()
{
    struct stu s[3] = { 0 };
    int i = 0;
    int n = sizeof(s) / sizeof(s[0]);
    set_stu_pro(s, n); //数组名传递

    for (i = 0; i < n; i++)
    {
        printf("%s, %d\n", s[i].name, s[i].age);//name000 20
    }
    return 0;
}

练习:输入圆的参数,返回较大的面积

cpp
#include<iostream>
typedef struct
{
    int x;
    int y;
    int r;
} circle;

void read(circle* p_circle)
{
    std::cout << "请输入一个圆的参数:";
    std::cin >> p_circle->x >> p_circle->y >> p_circle->r;
}

float area(const circle* p_c)
{
    float area = 3.14 * p_c->r * p_c->r;
    return area;
}

float cmp(float area1, float area2)
{
    if (area2 > area1)
    {
        area1 = area2;
    }
    return area1;
}

int main()
{
    circle c1 = { 0 };
    circle c2 = { 0 };
    read(&c1);
    read(&c2);
    float area1 = area(&c1);
    float area2 = area(&c2);
    float max_area = cmp(area1, area2);
    std::cout << "较大的面积是" << max_area << std::endl;
    return 0;
}

使用new和delete

cpp
#include<iostream>
typedef struct
{
    int x;
    int y;
    int r;
} circle;

circle* read()
{
    circle* c = new circle;
    if (c)
    {
        std::cout << "请输入一个圆的参数:";
        std::cin >> c->x >> c->y >> c->r;
        return c;
    }
    return NULL;
}

float area(const circle* p_c)
{
    float area = 3.14 * p_c->r * p_c->r;
    return area;
}

float cmp(float area1, float area2)
{
    if (area2 > area1)
    {
        area1 = area2;
    }
    return area1;
}

int main()
{
    circle* p_c1 = read();
    circle* p_c2 = read();
    float area1 = area(p_c1);
    float area2 = area(p_c2);
    float max_area = cmp(area1, area2);
    std::cout << "较大的面积是" << max_area << std::endl;

    delete p_c1;
    p_c1 = NULL;
    delete p_c2;
    p_c2 = NULL;
    return 0;
}

1.5 结构体的大小

结构体变量占用的内存空间永远是所有成员中占用内存最大成员的倍数(对齐问题)。

cpp
//结构体类型的定义
struct Person
{
    int age; // 4
    char ch; // 1
    double score; // 8
};

int main()
{
    struct Person p;
    std::cout << "sizeof(p) = " << sizeof(p) << std::endl; //16
    return 0;
}
  • 占用内存最大属性是score,占8个字节,所以第一次会分配8个字节
  • 将第一次分配的8个字节分配给age4个,分配给ch1个,还剩下3个字节
  • 当需要分配给score时,发现只剩下3个字节,所以会再次开辟8个字节存储空间
  • 一共开辟了两次8个字节空间,所以最终p占用16个字节
cpp
//结构体类型的定义
struct Person
{
    int age; // 4
    double score; // 8
    char ch; // 1
};

int main()
{
    struct Person p;
    std::cout << "sizeof(p) = " << sizeof(p) << std::endl; //24
    return 0;
}
  • 占用内存最大属性是score,占8个字节,所以第一次会分配8个字节
  • 将第一次分配的8个字节分配给age4个,还剩下4个字节
  • 当需要分配给score时,发现只剩下4个字节,所以会再次开辟8个字节存储空间
  • 将新分配的8个字节分配给score,还剩下0个字节
  • 当需要分配给ch时,发现上一次分配的已经没有了,所以会再次开辟8个字节存储空间
  • 一共开辟了3次8个字节空间,所以最终p占用24个字节

1.6 柔性数组

C99中,结构体中的最后一个元素允许是未知大小的数组,这就叫作柔性数组。大小可以在[]中写0,也可不写。结构体中柔性数组成员前面必须至少有一个其他成员

cpp
#include <iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

struct student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高
    int arr[/*0*/];
};

int main()
{
    return 0;
}
  • sizeof返回的这种结构大小不包括柔性数组的内存
    这里计算包含柔性数组的结构体大小,因为柔性数组本身是无法确定有几个字节的,所以计算整体结构体大小时,会省略柔性数组的计算。
cpp
#include <iostream>
#include<string>

typedef int INT;
typedef float FLOAT;

struct student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高
    int arr[/*0*/];
};

int main()
{
    std::cout << "sizeof(student) = "<< sizeof(student) << std::endl; //20
    return 0;
}
  • 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
    比如说我现在要数组arr里面有5个元素,现在进行malloc一下
cpp
#include <iostream>
#include<stdlib>

typedef int INT;
typedef float FLOAT;

struct student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高
    int arr[];
};

int main()
{
    std::cout << "sizeof(student) = "<< sizeof(student) << std::endl; //20
    struct student* stu = (struct student*)malloc(sizeof(student) + 5 * sizeof(int));
    strcpy(stu->name, "abc");
    stu->age = 18;
    stu->height = 181.1f;

    for (int i = 0; i < 5; i++)
    {
        stu->arr[i] = i + 1;
    }
    for (int i = 0; i < 5; i++)
    {
        std::cout << stu->arr[i] << " "<< std::endl;
    }
    free(stu);
    stu = NULL;
    return 0;
}

当柔性数组长度不够时,可以使用realloc修改申请的空间大小

cpp
#include <iostream>
#include<cstdlib>

typedef int INT;
typedef float FLOAT;

struct student
{
    char name[10]; // 姓名
    INT age; // 年龄
    FLOAT height; // 身高
    int arr[];
};

int main()
{
    std::cout << "sizeof(student) = " << sizeof(student) << std::endl; //20
    struct student* stu = (struct student*)malloc(sizeof(student) + 5 * sizeof(int));
    if (!stu)
    {
        std::cout << "申请失败" << std::endl;
    }
    strcpy(stu->name, "abc");
    stu->age = 18;
    stu->height = 181.1f;

    for (int i = 0; i < 5; i++)
    {
        stu->arr[i] = i + 1;
    }
    for (int i = 0; i < 5; i++)
    {
        std::cout << stu->arr[i] << " " << std::endl;
    }

    struct student* new_stu = (struct student*)realloc(stu, sizeof(student) + 10 * sizeof(int));
    if (new_stu == NULL)
    {
        // 内存重新分配失败,处理错误
        std::cout << "Memory allocation failed." << std::endl;
        free(stu); //不要忘记释放原内存
        return -1;
    }
    stu = new_stu;
    strcpy(stu->name, "def");
    stu->age = 20;
    stu->height = 120.1f;

    for (int i = 0; i < 10; i++)
    {
        stu->arr[i] = i + 1;
    }
    for (int i = 0; i < 10; i++)
    {
        std::cout << stu->arr[i] << " " << std::endl;
    }

    free(stu);
    stu = NULL;
    return 0;
}
  • 当然也可以使用指针方式定义该数组,但是此时需要内存释放两次,有可能会遗漏,造成内存泄露。
  • 柔性数组的好处:
    • 方便内存释放
      如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。以上,如果我把结构体的内存及其成员要的内存一次性分配好,并返回给用户一个结构体指针,用户做一次free就可以把所有内存都释放掉,并且不用考虑前面说的释放的顺序。
    • 加快访问速度
      连续的内存有益于提高访问速度,也有益于减少内存碎片。内存碎片、因为结构体的对齐原则,会造成内存浪费,即碎片化。

2. 联合

在C++中,有一个和结构体十分类似的数据类型,叫联合。与结构体相比,它更省内存空间。

格式:

cpp
union 联合名
{
    类型名1 成员名1;
    类型名2 成员名2;
    ……
    类型名n 成员名n;
};
cpp
#include <iostream>

//共用体也叫联合体
union Test
{
    unsigned short a;
    unsigned int b;
    double c;
};

int main()
{
    //定义共用体变量
    union Test tmp;

    //1. 所有成员的首地址是一样的
    std::cout << "&(tmp.a) = " << &(tmp.a) << std::endl;
    std::cout << "&(tmp.b) = " << &(tmp.b) << std::endl;
    std::cout << "&(tmp.c) = " << &(tmp.c) << std::endl;

    //2. 共用体大小为最大成员类型的大小
    std::cout << "sizeof(Test) = "<< sizeof(Test) << std::endl;

    return 0;
}
  1. 联合union是一个能在同一个存储空间存储不同类型数据的类型;
  2. 联合体所占的内存长度等于其最长成员的长度,也有叫做共用体;
  3. 共用体变量的地址和它的各成员的地址都是同一地址。

他还有其他的特点:

  1. 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
  2. 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
cpp
#include <iostream>
#include <stdio.h>

//共用体也叫联合体
union Test
{
    unsigned short a;
    unsigned int b;
    unsigned char c;
};

int main()
{
    //定义共用体变量
    union Test tmp;

    //1、所有成员的首地址是一样的
    std::cout << "&(tmp.a) = "<< &(tmp.a) << std::endl;
    std::cout << "&(tmp.b) = "<< &(tmp.b) << std::endl;

    //2、共用体大小为最大成员类型的大小
    std::cout << "sizeof(Test) = "<< sizeof(Test) << std::endl;

    tmp.b = 0x11223344; //左边是高位,右边是低位
    printf("tmp.a = %x\n", tmp.a);
    printf("tmp.b = %x\n", tmp.b);
    printf("tmp.c = %x\n", tmp.c);

    tmp.c = 0x55;
    printf("tmp.a = %x\n", tmp.a);
    printf("tmp.b = %x\n", tmp.b);
    printf("tmp.c = %x\n", tmp.c);

    return 0;
}
  • 大端存储和小端存储
    • 大端存储模式指的是内存的低位置存储于内存的高位置,数据的高位存储在内存的低位置。(低位放高地址)
    • 小端存储模式则指的是数据的低位存储在内存的低位置,数据的高位存储在内存的高位置中。(低位放低地址)

低地址取到了低位,因此是小端存储(大部分电脑都是小端存储)

cpp
tmp.b = 0x11223344; //左边是高位,右边是低位
printf("tmp.c = %x\n", tmp.c); //44

3. 枚举

枚举:就是列举,将一些可能的常量列举出来。

格式:

cpp
enum 枚举名
{
    枚举元素1,
    枚举元素2,
    ...
};

示例:

cpp
// 表示一年四季
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
};

枚举变量,先定义枚举类型,再定义枚举变量

cpp
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
};
enum Season s;

定义枚举类型的同时定义枚举变量

cpp
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
} s;

省略枚举名称,直接定义枚举变量

cpp
enum
{
    Spring,
    Summer,
    Autumn,
    Winter
} s;

C++编译器会将枚举元素(spring, summer等)作为整型常量处理,称为枚举常量。
枚举元素的值取决于定义时各枚举元素排列的先后顺序。默认情况下,第一个枚举元素值为0,第二个为1,依次顺序加1。

cpp
#include <iostream>
enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
} s; // 也就是说spring的值为0,summer的值为1,autumn的值为2,winter的值为3

int main()
{
    s = Winter;
    std::cout << "s = " << s << std::endl; // 3
    return 0;
}

也可以在定义枚举类型时改变枚举元素的值

cpp
#include <iostream>
enum Season
{
    Spring,
    Summer = 9,
    Autumn,
    Winter // 也就是说spring的值为0,summer的值为9,autumn的值为10,winter的值为11
} s;

int main()
{
    s = Winter;
    std::cout << "s = " << s << std::endl; // 11
    //s = 1;//c语言允许这么做用,但是c++不允许
    return 0;
}

知识如风,常伴吾身