第十章-构造类型

结构体

// 1.定义结构体类型
    struct Person
    { // 里面的3个变量,可以称为是结构体的成员或者属性
        int age; // 年龄
        double height; // 身高
        char *name; // 姓名
    };
    // 2.根据结构体类型,定义结构体变量
    //定义时初始化,也可以先定义在初始化
    struct Person p = {20, 1.55, "jack"};
    p.age = 30;
    p.name = "rose";
    //结构体变量的取值
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);
    
    /* 错误写法
    struct Person p2;
    p2 = {30, 1.67, "jake"};
     //只能分别赋值初始化
     p2.age = 30
    */
    //另一种初始化方法(可以不按顺序赋值)
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};
    //p2.age = 25;

结构体内存

// 1.定义结构体类型(并不会分配存储空间)
    struct Date
    {
        int year;
        int month;
        int day;
    };
    // 2.定义结构体变量(真正分配存储空间)
    struct Date d1 = {2011, 4, 10};
    struct Date d2 = {2012, 8, 9};
    // 会将d1所有成员的值对应地赋值给d2的所有成员
    d2 = d1;
    //d1仍然不变
    d2.year = 2010;
    
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);
    printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day);
     int s = sizeof(d1);
     //12
     printf("%d\n", s);
struct Student
    {
        int age;// 4个字节
        char *name; // 8个字节
    };
    
    struct Student stu;
    stu.age = 20;
    stu.name = "jack";
    // 补齐算法(对齐算法)
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数
    int s = sizeof(stu);
    //为16并不是12
    printf("%d\n", s);

定义结构体变量的几种方式

  1. 定义结构体变量的3种方式
    • 先定义类型,再定义变量(分开定义)
    struct Student
     {
     int age;
     };
     struct Student stu;
    
    • 定义类型的同时定义变量
    struct Student
     {
     int age;
     } stu;
     //再定义一个变量
     struct Student stu2;
    
    • 定义类型的同时定义变量(省略了类型名称,不能重用)
    struct
     {
     int age;
     } stu;
    
  2. 结构体类型的作用域
    • 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
    • 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)

结构体数组

  //1.定义结构体类型
    struct RankRecord
    {
        int no; // 序号  4
        int score; // 积分 4
        char *name; // 名称 8
    };
    //2.定义结构体数组,并初始化
    struct RankRecord records[3] =
    {
        {1, "jack", 5000},
        
        {2, "jim", 500},
        
        {3, "jake",300}
    };
    //赋值
    records[0].no = 4;
    // 错误写法
    //records[0] = {4, "rose", 9000};
    
    for (int i = 0; i<3; i++)
    {
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);
    }

结构体指针

  1. 指向结构体的指针的定义
    • struct Student *p;
  2. 利用指针访问结构体的成员
    • (*p).成员名称
    • p->成员名称
//1.定义结构体类型
struct Student
{
    int no;
    int age;
};
//2. 结构体变量初始化
struct Student stu = {1, 20};
    
//3. 指针变量p将来指向struct Student类型的数据
struct Student *p;
    
// 指针变量p指向了stu变量
p = &stu;
    
p->age = 30;
// 利用指针访问结构体的成员
// 第一种方式
printf("age=%d, no=%d\n", stu.age, stu.no);
    
// 第二种方式
printf("age=%d, no=%d\n", (*p).age, (*p).no);
    
// 第三种方式
printf("age=%d, no=%d\n", p->age, p->no);

结构体和函数

#include <stdio.h>
struct Student
{
    int age;
    int no;
};

// 如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员
// 修改函数内部结构体的成员不会影响外面的实参结构体
void test(struct Student s)
{
    s.age = 30;
    s.no = 2;
}

// 会影响外面的实参结构体
void test2(struct Student *p)
{
    p->age = 15;
    p->no = 2;

}
//不会影响
void test3(struct Student *p)
{
    struct Student stu2 = {15, 2};
    p = &stu2;
    p->age = 16;
    p->no = 3;
}

int main()
{
    struct Student stu = {28, 1};
    
    //test(stu);
    //test2(&stu);
    test3(&stu);
    
    printf("age=%d, no=%d\n", stu.age, stu.no);
    
    return 0;
}

结构体的嵌套定义

#include <stdio.h>

int main()
{
    struct Date
    {
        int year;
        int month;
        int day;
    };
    
    // 类型
    struct Student
    {
        int no; // 学号
        
        struct Date birthday; // 生日
        
        struct Date ruxueDate; // 入学日期
        
        // 这种写法是错误的,不允许对结构体本身递归定义
        //struct Student stu;
        //但是允许结构体递归指向
        struct Student *stu;
    };
    
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};
    
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);
    
    return 0;
}

深拷贝和浅拷贝

  1. 浅拷贝:直接赋值,指向同一块内存

     //结构体定义
     struct  stringg
     {
     	char *p;
     	int length;
        
     };
        
     //浅拷贝:指向同一块内存,str1,改变,str2也改变
     void test7(){
     	struct stringg str1;
     	str1.length = 10;
     	str1.p = (char *)malloc(sizeof(char)* 10);
     	strcpy(str1.p, "helloc");
     	printf("%s\n", str1.p);
     	struct stringg str2;
     	//直接赋值,指向同一块内存
     	str2.p = str1.p;
     	str2.length = str1.length;
     	//修改str1
     	(*str1.p) = 'k';
     	//str2.p也改变了,这就是浅拷贝---kelloc
     	printf("%s\n", str2.p);
     }
    
  2. 深拷贝:拷贝内存内容,指向不同内存

     struct stringg str1;
     str1.length = 10;
     str1.p = (char *)malloc(sizeof(char)* 10);
     strcpy(str1.p, "helloc");
     printf("%s\n", str1.p);
     struct stringg str2;
     str2.p = (char *)malloc(sizeof(char)* 10);
     //深拷贝拷贝内存内容
     strcpy(str2.p, str1.p);
     str2.length = str1.length;
     //修改str1
     (*str1.p) = 'k';
     //str2.p不影响,这就是深拷贝---helloc
     printf("%s\n", str2.p);
    

枚举

  //对于枚举类型中元素的默认:全部是是整型常量,默认从0开始,0,1,2,3
    enum Sex { Man, Woman, Unkown};
    enum Sex s = Unkown;
    
    // 1.定义枚举类型
    //元素从1开始:1,2,3,4
    enum Season
    {
        spring = 1,
        summer,
        autumn,
        winter
    };
    // 2.定义枚举变量
    enum Season s = summer;
    printf("%d\n", s);
    

数据类型的总结

Table of Contents