结构体

是用户自定义的类型,可以将多种数据的表示合并在一起描述一个完整的对象

定义结构体

语法:

struct 结构体名{
    成员一的数据类型   成员一名;
    成员二的数据类型   成员二名;
    ……                  ……;
};

  • 结构体名为标识符

  • 结构体成员可以是任意数据类型

  • 定义结构体的代码可以放在任意位置,但一般建议放在main函数上面或者头文件中

  • 结构体成员可以用C++的类(例如String),但不提倡

  • C++中,结构体中可以使用函数,但不提倡

  • C++中,定义结构体时可以指定缺省值

    struct 结构体名{
        成员一的数据类型   成员一名 = 缺省值;
        成员二的数据类型   成员二名 = 缺省值;
        ……                  ……        ……   ;
    };
    

创建结构体变量

语法:

struct  结构体名  结构体变量名;
结构体名  结构体变量名;     //C++标准
//赋初值
struct  结构体名  结构体变量名 = {成员一的值,成员二的值,……};
struct  结构体名  结构体变量名 = {0};        //将全部成员的值设为0
结构体名  结构体变量名 {0};            //将全部成员的值设为0    C++11标准

⚠在C++中,struct​​关键字可以不写

⚠可以在定义结构体的事后创建结构体变量名和赋初值

struct 结构体名{
    成员一的数据类型   成员一名 = 缺省值;
    成员二的数据类型   成员二名 = 缺省值;
    ……                  ……        ……   ;
}结构体变量名 = {成员一的值,成员二的值,……};

使用结构体

访问结构体

结构体变量名.结构体成员名
//例子
struct Stu{
    char name[20];
    int age;
}students;
students.name = "zhangsan";
students.age = 20 ;

清空结构体(只适用于C++基本数据类型)

创建的结构体变量未初始化,其值为垃圾值

memset(结构体地址,0,sizeof(结构体名));
bzero(结构体地址,sizeof(结构体名));        //添加头文件<cstring>

复制结构体(只适用于C++基本数据类型)

1、memcpy(目标结构体地址,源结构体地址);
2、直接用" = "
//例子
struct stu1{
    char name[20];
    int age;
};
stu1 s1 ={"zhangsan",20};
stu1 s2;
//复制
memcpy (s2,s1);

结构体占用内存大小

​sizeof(结构体名)​​得到整个结构体占用内存大小

整个结构体占用的内存大小不一定等于各结构体成员占用的内存之和

💡原因:结构体内存对齐

结构体指针

声明结构体指针

步骤📈

  1. 声明结构体变量

  2. 声明结构体指针,指向结构体变量

struct 结构体名 *指针名 = 结构体变量名的地址

⚠结构体变量名未被解释为地址,结构体变量名的地址应为&结构体变量名​​

通过结构体指针访问结构体成员

方法一:指针名∗指针名.结构体成员名​​

❗在方法一种括号不能去掉,因为' . '的优先级高于' * '

方法二📣:指针名->结构体成员名​​

struct stu1{
    char name[20];
    int age;
}s1;
stu1 *ptr = &s1;     //声明结构体指针
//方法一
(*ptr).name = "zhangsan";
(*ptr).age = 20;
//方法二
ptr->name = "zhangsan";
ptr->age = 20;

用途

  • 用于函数的参数

    struct stu1{
        char name[20];
        int age;
    }s1;
    void func(stu1 *ptr){
        std::cout << "学生名字:"<<ptr->name<<"  学生年龄:"<< ptr->age <<"岁"<<std::endl;
        std::cout << "学生名字:"<<(*ptr).name<<"  学生年龄:"<< (*ptr).age <<"岁"<<std::endl;
    }
    int main(){
        stu1 s1 = {"zhangsan",20};
        func(&s1);
    }
    
  • 用于动态分配内存

    struct stu1{
        char name[20];
        int age;
    };
    int main(){
        stu1 *ptr = new stu1 ;
        memset(ptr,0,sizeof(stu1));
        std::cout << "学生名字:"<<ptr->name<<"  学生年龄:"<< ptr->age <<"岁"<<std::endl;
        //上下等价
        stu *ptr2 = new stu1({0});
        std::cout << "学生名字:"<<ptr2->name<<"  学生年龄:"<< ptr2->age <<"岁"<<std::endl;
    
        delete ptr;    //动态分配要释放内存
        delete ptr2;
    }
    

⚠结构体指针初始化使用memset函数时

memsetptr,0,sizeof(ptrptr,0,sizeof(ptr) ❎

memsetptr,0,sizeof(stu1ptr,0,sizeof(stu1) ✅

原因: ptr为指针,sizeof指针指针任何时候都为8,而不是结构体的内存大小

结构体数组

声明结构体数组

语法:

struct  结构体类型    数组名[数组长度]

初始化结构体数组

struct stu1{
    char name[20];
    int age;
};
struct stu1 students[2] = {{"zhangsan",20},{"lisi",19}};
stu1 students[2] = {{"zhangsan",20},{"lisi",19}};        //C++可以不用写struct

访问结构体数组

  • 数组表示法(用的多)

    struct stu1{
        char name[20];
        int age;
    };
    stu1 students[2] = {0};
    strcpy(students[0].name ,"zhangsan");    //名字为char数组,只能用strcpy()赋值,不能用' = '
    students[0].age =20;
    
  • 指针表示法(用的少)

    struct stu1{
        char name[20];
        int age;
    };
    stu1 students[2] = {0};
    strcpy(students->name ,"zhangsan");    //名字为char数组,只能用strcpy()赋值,不能用' = '
    students->age =20;
    

结构体中嵌入数组或者结构体

结构体中嵌入一维数组

struct s1{
    char name[20];
    int tel[20];
    int age;
}stu;
//访问
stu.tel[0]= 1;
stu.tel[1]= 5;
stu.tel[2]= 2;

结构体中嵌入二维数组

把二维数组放入结构体中作为一个成员,调用函数时,把结构体的地址传给函数,函数的形参用指针接收结构体的地址

struct stu{
    int score[2][3]= {{1,2,3},{4,5,6}};
}s1;
void func (stu * ptr){
    for ( int ii=0 ; ii<2 ; ii++){
        for ( int jj=0 ; jj<3 ; jj++)
            std::cout << ptr->score[ii][jj] <<std::endl;
    }
}
int main(){
    func(&s1);
}

结构体中嵌入其他结构体

⚠结构体中不能够嵌套自身

struct st_pet{
    char name[20];
    int petage;
};
struct st_girl{
    char name[20];
    int age;
    struct st_pet pet;
};
//初始化或赋值
st_girl girl = {"zhangsan", 20,"huahua",5};
或
st_girl girl = {"zhangsan", 20,{"huahua",5}};

结构体中的指针

如果结构体中的指针指向的是动态分配的内存地址

  • 对结构体使用sizeof运算符可能毫无意义

    struct st_stu{
        int a;
        int *p;
    };
    int main(){
        st_stu s1;
        s1.p = new int [100] ;     //结构体中的p指针指向动态分配的内存地址
        std::cout << "sizeof(s1) = " <<sizeof(s1) << std::endl;
    }
    //运行结果
    sizeof(s1) = 16
    

    sizeof运算符得到的只包括了结构体中的int型和int指针的内存大小,而不包括指针p指向的动态分配的内存,这是不合理的

  • 对结构体用memset函数可能会造成内存泄露

    struct st_stu{
        int a;
        int *p;
    };
    int main(){
        st_stu s1;
        s1.p = new int [100] ;     //结构体中的p指针指向动态分配的内存地址
    
        //std::cout << "使用前*p= " <<s1.p << std::endl;    
        //memset(&s1,0,sizeof(st_stu));    //只会清空指针
        //std::cout << "使用后*p= " <<s1.p << std::endl;
    
        //正确清空结构体应该逐个清空
        std::cout << "使用前*p= " <<s1.p << std::endl;
        s1.a = 0;     //清空int a成员变量
        //正确清空p指针指向的内容
        memset(s1.p,0,100*sizeof(int));
         std::cout << "使用后*p= " <<s1.p << std::endl;
    }
    //运行结果
    //使用前*p= 0x1ade9111bc0
    //使用后*p= 0
    使用前*p= 0x1e436c71ab0
    使用后*p= 0x1e436c71ab0
    

    在C++中,动态分配的内存只能使用指针来访问,如果使用memset&s1,0,sizeof(st_stu&s1,0,sizeof(st_stu)​​只会清空指针,这会导致两个问题

    1. 指针p变为空指针,后续对指针p的操作可能会导致程序崩溃

    2. 指针p指向的动态分配的内存未被释放,可能会导致内存泄露

  • C++字符串String中有一个指向的是动态分配的内存地址的指针,在结构体中使用应注意

    struct st_stu{
        std::string name;
    };
    int main(){
        st_stu s1 = {"zhangsan"};
        std::cout << "姓名:= " <<s1.name << std::endl;
    
        memset(&s1,0,sizeof(s1));
    
        std::cout << "姓名: " <<s1.name << std::endl;
    }
    //运行结果
    姓名: zhangsan
    姓名:            //不正常输出
    

共同体

共同体能存储不同的数据类型,但是在同一时间只能存储其中的一种类型

定义语法

union 共同体名
{
    成员一的数据类型   成员一名;
    成员二的数据类型   成员二名;
    …………              ……    ;
    成员N的数据类型   成员N名;
};

注意

  • 共同体占用的内存大小为其中最大的成员占用的内存大小(内存对齐)

    union un_1{
        int a;
        char b;
        double c;
    };
    std::cout << "sizeof(un_1) = " << sizeof(un_1) <<std::endl;
    //运行结果
    sizeof(un_1) = 8
    
  • 全部成员使用同一块内存

    union un_1{
            int a;
            char b;
            double c;
        };
        un_1 n1;
        std::cout << "(void*)(&n1.a) = " << (void*)(&n1.a) <<std::endl;
        std::cout << "(void*)(&n1.b) = " << (void*)(&n1.b) <<std::endl;
        std::cout << "(void*)(&n1.c) = " << (void*)(&n1.c) <<std::endl;
    //运行结果
    (void*)(&n1.a) = 0x1f5adffcf0
    (void*)(&n1.b) = 0x1f5adffcf0
    (void*)(&n1.c) = 0x1f5adffcf0
    
  • 共同体中的值为最后被赋值的那个成员的值

    union un_1{
            int a;
            char b;
            double c;
        };
        un_1 n1;
        n1.a = 4;
        n1.c = 8;
        std::cout << "n1.a = "<< n1.a <<std::endl;
        std::cout << "n1.b = "<< n1.b <<std::endl;
        std::cout << "n1.c = "<< n1.c <<std::endl;
    //运行结果
    n1.a = 0        //不正常显示
    n1.b =        //不正常显示
    n1.c = 8        //正常显示
    
  • 匿名共同体没有名字,可以在定义时创建共同体变量,也可以嵌入结构体中

    union
    {
        int a;
        char b;
        double c;
    }n1;
    //Windows的VS下编译时报错
    //Linux的GCC下编译时警告
    

枚举

枚举是一种创建符号常量的方法

枚举的语法

enum 枚举名{枚举量1,枚举量2,……枚举量n};
//例如
enum colors {red , yellow , blue};    //创建枚举类型
colors cc = red ;                     //创建枚举变量,并赋初始值

​enum colors {red , yellow , blue};​​ colors成为了一种新的枚举类型名称,用它可以创建枚举变量;red、yellow、blue作为符号常量,默认值为0,1,2

enum colors {red , yellow , blue};
std::cout << "red = " << red << ",yellow = " << yellow << ",blue = " << blue <<std::endl;
//运行结果
red = 0,yellow = 1,blue = 2

注意事项

  • 用枚举创建的变量取值只能在枚举量范围内

  • 枚举的作用域与枚举变量的作用域相同

  • 可以显式的设置枚举量的值必须为整数必须为整数

    enum colors {red=1 , yellow =2, blue =3};
    std::cout << "red = " << red << ",yellow = " << yellow << ",blue = " << blue <<std::endl;
    //运行结果
    red = 1,yellow = 2,blue = 3
    
  • 可以显式的设置指定的某些枚举量的值枚举量的值可以重复枚举量的值可以重复

    enum colors {red , yellow =0, blue };
    std::cout << "red = " << red << ",yellow = " << yellow << ",blue = " << blue <<std::endl;
    //运行结果
    red = 0,yellow = 0,blue = 1
    
    enum colors {red , yellow =6, blue };
    std::cout << "red = " << red << ",yellow = " << yellow << ",blue = " << blue <<std::endl;
    //运行结果
    red = 0,yellow = 6,blue = 7
    
    enum colors {red=6 , yellow, blue };
    std::cout << "red = " << red << ",yellow = " << yellow << ",blue = " << blue <<std::endl;
    //运行结果
    red =6,yellow = 7,blue = 8
    
  • 可以将整数强制转化为枚举量,语法为 枚举类型整数整数

    enum colors {red , yellow , blue};
    color cc = 1 ;            //报错,不能将1赋给枚举变量,只能为red,yellow,blue
    color cc = colors(1) ;