1、why结构体?
#include <stdio.h>
int main()
{
/*
* 什么是结构体?
* 结构体时构造类型的一种
*
* 构造类型前面我们已经学习过了数组:
* 数组的作用是用于存储一组相`同类型`的数据
* 结构体的作用是用于存储一组`不同类型`的数据
*
* 保存一个人的信息
* 姓名/年龄/身高 ...
* char *
* int
* double
*
* 如何定义结构体变量
* 1.先定义结构体类型
* 2.通过结构体的类型定义结构体变量
*
* 如何定义结构体类型?
* struct 结构体类型名称{
* 数据类型 属性名称;
* 数据类型 属性名称;
* ... ...
* };
*
* 如何定义结构体变量
* struct 结构体类型名称 变量名称;
*
* 如何访问结构体的属性
* 结构体变量名称.结构体属性名称;
*/
// 1.定义结构体类型
struct Person{
char *name; // name我们称之为结构体的属性名称
int age; // age也称之为结构体的属性名
double height; // height也称之为结构体的属性名称
};
// 2.定义结构体变量
struct Person p;
// 3.使用结构体变量
// int ages[3] = {1, 3, 5};
// ages[0] = 1;
// 格式: 结构体变量名称.结构体属性名称
p.name = "msh";
p.age = 24;
p.height = 2.0;
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
return 0;
}
2、结构体的初始化
#include <stdio.h>
int main()
{
/*
* 结构体变量初始化的几种方式
* 1.定义的同时初始化
* 2.先定义再初始化
*/
// 1.定义结构体类型
struct Dog{
char *name;
int age;
double height;
};
// 2.定义结构体变量
// 2.1先定义后初始化
// struct Dog dd;
// dd.name = "ww";
// dd.age = 1;
// dd.height = 1.5;
// 2.2定义的同时初始化
// struct Dog dd = {"ww", 1, 1.5};
// 注意点: 如果在定义的同时初始化, 那么初始化的顺序必须和结构体类型中的顺序一致
// struct Dog dd = {1, "ww", 1.5};
// struct Dog dd = {.age = 1, .name = "ww", .height = 1.5};
// printf("name = %s\n", dd.name);
// printf("name = %i\n", dd.age);
// printf("name = %lf\n", dd.height);
// 3.特殊的初始化方式
// 数组只能在定义的同时完全初始化, 不能先定义再完全初始化
// 但是结构体既可以在定义的同时完全初始化, 也可以先定义再完全初始
// int ages[3] = {1, 3, 5};
// int ages[3];
// ages = {1, 3, 5};
// 企业开发不推荐这样编写
struct Dog dd;
dd = (struct Dog){"ww", 1, 1.5};
printf("name = %s\n", dd.name);
printf("name = %i\n", dd.age);
printf("name = %lf\n", dd.height);
return 0;
}
3、结构体的几种定义方式
#include <stdio.h>
int main()
{
/*
* 定义结构体变量的几种方式
* 1.先定义结构体类型, 再定义结构体变量
* 2.定义结构体类型的同时定义结构体变量
* 3.定义结构体类型的同时省略结构体名称, 同时定义结构体变量
*/
// 1.先定义结构体类型, 再定义结构体变量
/*
struct Person{
char *name;
int age;
double height;
};
struct Person p1;
struct Person p11;
*/
// 2.定义结构体类型的同时定义结构体变量
/*
struct Person{
char *name;
int age;
double height;
} p2;
p2.name = "msh";
printf("name = %s\n", p2.name);
struct Person p22;
*/
// 3.定义结构体类型的同时省略结构体名称, 同时定义结构体变量
// 匿名结构体
// 特点: 结构体类型只能使用一次
struct{
char *name;
int age;
double height;
} p3;
p3.name = "yl888";
printf("name = %s\n", p3.name);
return 0;
}
#include <stdio.h>
struct Person{
char *name;
int age;
double height;
};
int main()
{
/*
* 结构体类型的作用域
* 和变量的作用域一样
*/
// 1.定义一个结构体类型
{
// struct Person{
// char *name;
// int age;
// double height;
// };
struct Person p1;
}
struct Person p3;
return 0;
}
void test(){
// 找不到Person这个结构体类型
struct Person p2;
}
#include <stdio.h>
int main()
{
/*
* 结构体数组
* 就是定义个数组保存结构体变量
*/
struct Person{
char *name;
int age;
double height;
};
// struct Person p1 = {"msh", 24, 1.90};
// struct Person p2 = {"zs", 22, 1.2};
// struct Person p3 = {"ls", 33, 1.4};
// struct Person p4 = {"ww", 56, 1.8};
// 数据类型 数组名称[元素个数];
// struct Person ps[4];
// ps[0] = p1;
// ps[1] = p2;
// ps[2] = p3;
// ps[3] = p4;
// struct Person ps[4] = {p1, p2, p3, p4};
struct Person ps[4] ={
{"msh", 24, 1.90},
{"zs", 22, 1.2},
{"ls", 33, 1.4},
{"ww", 56, 1.8},
};
return 0;
}
int main()
{
/*
* 结构体内存分析
* 注意点:
* 给整个结构体变量分配存储空间和数组一样, 从内存地址比较大的开始分配
* 给结构体变量中的属性分配存储空间也和数组一样, 从所占用内存地址比较小的开始分配
*
* 注意点:
* 和数组不同的是, 数组名保存的就是数组首元素的地址
* 而结构体变量名, 保存的不是结构体首属性的地址
*/
/*
struct Person{
int age;
int score;
};
struct Person p;
// 看上去, 好像是所有属性类型的总和
printf("sizeof = %i\n", sizeof(p)); // 8
// 数组名称保持的就是数组首元素的地址
// 结构体的名称是否保存的也是第一个属性的地址
// 通过观察得出一个结论: 结构体变量的名称并没有保存结构体首属性的地址
printf("p = %p\n", p); // p = 00000077
printf("&p = %p\n", &p); // &p = 0060FEA8
printf("&p.age = %p\n", &p.age); // &p.age = 0060FEA8
printf("&p.score = %p\n", &p.score); // &p.score = 0060FEAC
*/
struct Person{
int age; // 4
// char ch; // 1
double score; // 8
char ch;
};
struct Person p;
// 结构体在分配内存的时候, 会做一个内存对齐的操作
// 会先获取所有属性中占用内存最大的属性的字节
// 然后再开辟最大属性字节的内存给第一个属性, 如果分配给第一个属性之后还能继续分配给第二个属性, 那么就继续分配
// 如果分配给第一个属性之后, 剩余的内存不够分配给第二个属性了, 那么会再次开辟最大属性直接的内存, 再次分配
// 一次类推
printf("sizeof = %i\n", sizeof(p)); // 16
return 0;
}
#include <stdio.h>
int main()
{
/*
* 结构体指针
* 因为结构体变量也会分配内存空间, 所以结构体变量也有内存地址, 所以也可以使用指针保存结构体变量的地址
*
* 规律:
* 定义指向结构体变量的指针的套路和过去定义指向普通变量的一样
*
* 如果指针指向了一个结构体变量, 那么访问结构体变量的属性就有3种方式
* 结构体变量名称.属性名称;
* (*结构体指针变量名称).属性名称;
* 结构体指针变量名称->属性名称;
*/
struct Person{
char *name;
int age;
double height;
};
struct Person per = {"msh", 24, 1.9};
struct Person *p;
p = &per;
// printf("per.name = %s\n", per.name);
// printf("per.name = %s\n", (*p).name);
printf("per.name = %s\n", p->name);
return 0;
}
#include <stdio.h>
int main()
{
/*
* 结构体嵌套定义
* 结构体的属性可以又是一个结构体
*
* 提高代码的复用性
*/
/*
struct Person{
char *name;
int age;
// 出生年月
int year;
int month;
int day;
// 死亡日期
int year2;
int month2;
int day2;
// 读幼儿园日期
// 读小学日期
// 读中学日期
// 读大学日期
// 工作日期
// 结婚日期
// ... ...
}
*/
// 1.定义了一个日期的结构体类型
struct Date{
int year;
int month;
int day;
};
// 2.定义一个人的结构体类型
struct Person{
char *name;
int age;
struct Date birthday;
};
struct Person p = {"msh", 24, {2020, 12, 12}};
printf("name = %s\n", p.name);
printf("name = %i\n", p.age);
printf("name = %i\n", p.birthday.year);
printf("name = %i\n", p.birthday.month);
printf("name = %i\n", p.birthday.day);
return 0;
}
#include <stdio.h>
struct Person{
char *name;
int age;
};
void test(struct Person per);
int main()
{
/*
* 结构体和函数
* 虽然结构体是构造类型, 但是结构体变量之间的赋值
* 和基本数据类型赋值一样, 是拷贝
*/
// 注意点: 定义结构体类型不会分配存储空间
// 只有定义结构体变量才会分配存储空间
/*
struct Person{
char *name;
int age;
};
struct Person p1 = {"msh", 24};
struct Person p2;
p2 = p1;
p2.name = "zs";
printf("p1.name = %s\n", p1.name); // msh
printf("p2.name = %s\n", p2.name); // zs
*/
struct Person p1 = {"msh", 24};
printf("p1.name = %s\n", p1.name); // msh
test(p1);
printf("p1.name = %s\n", p1.name); // msh
return 0;
}
void test(struct Person per){
per.name = "zs";
}