基本概念
基本数据类型:int double float char
构造类型:
- 数组(用于保存一组相同类型的数据)
- 结构体(用于保存一组不同类型的数据)
定义结构体
- 1.定义结构体类型
定义结构体类型不会分配内存存储空间,只有定义结构体变量的时候才会真正的分配内存存储空间 - 2.根据结构体类型,定义结构体变量
#include <stdio.h>
int main(int argc, const char * argv[]) {
// 定义结构体
// 1.定义结构体类型
struct Person {
int age; // 年龄
char *name; // 姓名
double height; // 身高
};
// 2.根据结构体类型,定义结构体变量
struct Person sp;
// 结构体变量所占内存存储空间,是由结构体中占用内存空间最大的属性决定的,每次开辟一块占用内存空间最大属性的存储空间给属性依次存放
/*
2.1 计算结构体 Person 中占用内存空间最大的属性
int age; 占用4个字节
char *name; 指针占用8个字节
double height; 占用8个字节
2.2 每次开辟8个字节的空间进行存储数据
第一次开辟8个字节的空间存储第一个属性 age
第一个属性存储完后,剩余4个字节,第二个属性进行存储需要占用8个字节,剩余的4个字节存储不下,则新开辟一块8个字节的空间进行存储
然后将第二个属性 *name 存储到第二次开辟的8个字节的空间中,第二个属性存储之后,没有剩余空间
第三次开辟一块新空间存储height
所以此时 sp 共占用24个字节
*/
sp.name = "fdsafdsafdsafdsfsafdsafdsafdsafdsafdsafsafdsaffdsafdsafdsafdsafdsa";
printf("sp size --> %lu\n", sizeof(sp)); // sp size --> 24
printf("sp --> %p\n", &sp); // sp --> 0x7ffeefbff518
printf("&sp.age --> %p\n", &sp.age); // &sp.age --> 0x7ffeefbff518
printf("&sp.name --> %p\n", &sp.name); // &sp.name --> 0x7ffeefbff520 指针的内存地址,占8个字节
printf("sp.name --> %p\n", sp.name); // sp.name --> 0x100000f12 指针中存储对应字符数组的内存地址
printf("&sp.height --> %p\n", &sp.height); // &sp.height --> 0x7ffeefbff528
return 0;
}
注意: 使用结构体中占用内存空间最大的属性进行开辟每次存储的新空间大小,主要是为了提高计算机的运行速度
结构体定义的3种方式
#include <stdio.h>
int main(int argc, const char * argv[]) {
// 1.先定义结构体类型,在定义结构体变量
struct Person1 {
int age;
char *name;
double height;
};
struct Person1 sp1;
sp1.name = "zhangsan1";
printf("sp1.name --> %s\n", sp1.name); // sp1.name --> zhangsan1
// 2.定义结构体类型的同时定义结构体变量
struct Person2 {
int age;
char *name;
double height;
} sp2;
sp2.name = "zhangsan2";
printf("sp2.name --> %s\n", sp2.name); // sp2.name --> zhangsan2
// 3.定义结构体类型的同时定义结构体变量,并且省略结构体名称
// 如果在定义结构体类型的同时定义结构体变量,那么可以省略结构体类型名称
// 弊端:由于结构体类型没有名称,所以以后就不能使用该结构体类型
// 优点:如果结构体类型只需要使用一次,那么可以使用该方式
struct {
int age;
char *name;
double height;
} sp3;
sp3.name = "zhangsan3";
printf("sp3.name --> %s\n", sp3.name); // sp3.name --> zhangsan3
return 0;
}
结构体初始化的4种方式
#include <stdio.h>
int main(int argc, const char * argv[]) {
struct Dog {
char *name;
int age;
double height;
};
// 1.定义的同时初始化
struct Dog sd1 = {"ww", 13, 5.1};
// 2.先定义再初始化(逐个初始化)
struct Dog sd2;
sd2.name = "ww";
sd2.age = 13;
sd2.height = 5.1;
// 3.先定义再初始化(一次性初始化)
/*
注意:结构体和数组有一点区别,数组不能先定义再进行一次性的初始化
结构体可以先定义再进行一次性的初始化
*/
struct Dog sd3;
sd3 = (struct Dog){"ww", 13, 5.1};
// 4.指定将数据赋值给指定的属性
struct Dog sd4 = {.height = 5.1, .name="ww", .age=13};
// 结构体属性输出
printf("name = %s, age = %i, height = %lf\n", sd4.name, sd4.age, sd4.height); // name = ww, age = 13, height = 5.100000
return 0;
}
结构体与指针
#include <stdio.h>
int main(int argc, const char * argv[]) {
struct Person {
char *name;
int age;
int height;
};
// 定义结构体变量
struct Person sp;
// 定义指向结构体变量的指针
struct Person *sip;
sip = &sp; // 将结构体的内存地址赋值给指针
// 使用指针给结构体属性赋值
// 方法1 (*指针名).属性名
(*sip).name = "xiaowang"; // 由于.的优先级比*的优先级高,所以要使用括号提高优先级
printf("sp.name -->%s\n", sp.name); // sp.name -->xiaowang
printf("(*sip).name -->%s\n", (*sip).name); // (*sip).name -->xiaowang
// 方法2 指针名->属性名
sip->name = "张三";
printf("sp.name -->%s\n", sp.name); // sp.name -->张三
printf("sip->name -->%s\n", sip->name); // sip->name -->张三
return 0;
}
结构体数组
#include <stdio.h>
int main(int argc, const char * argv[]) {
// 定义结构体
struct Bumen {
char *name; // 部门名称
int count; // 部门人数
double kpi; // 部门绩效kpi
};
// 定义结构体数组变量并初始化值
struct Bumen bumens[3] = {
{"IOS", 20, 100.0},
{"Andoird", 10, 90.0},
{"PHP", 100, 88.2},
};
// 修改结构体数组中对应的值
bumens[0].name = "Python";
bumens[0].count = 50;
bumens[0].kpi = 95;
printf("name --> %s, count --> %i, kpi --> %lf\n", bumens[0].name, bumens[0].count, bumens[0].kpi); // name --> Python, count --> 50, kpi --> 95.000000
return 0;
}
结构体嵌套
#include <stdio.h>
int main(int argc, const char * argv[]) {
// 结构体嵌套
// 定义时间结构体
struct Date {
int YY; // 年
int MM; // 月
int DD; // 日
};
struct Time {
int HH; // 时
int mm; // 分
int ss; // 秒
};
struct Datetime {
struct Date date; // 年月日
struct Time time; // 时分秒
};
struct Person {
char *name; // 姓名
int age; // 年龄
struct Datetime birth; // 出生时间
struct Date ruxue; // 小学入学时间
struct Date biye; // 小学毕业时间
};
struct Person sp = {
"张三",
16,
{
{1990, 10, 5},
{11, 2, 6},
},
{1997, 9, 01},
{2003, 07, 01},
};
printf("sp.name --> %s\n", sp.name);
printf("sp.age --> %i\n", sp.age);
printf("sp.birth.datetime --> %i-%i-%i %02i:%02i:%02i\n", sp.birth.date.YY, sp.birth.date.MM, sp.birth.date.DD, sp.birth.time.HH, sp.birth.time.mm, sp.birth.time.ss); // sp.birth.datetime --> 1990-10-5 11:02:06
return 0;
}
结构体作为函数参数
结构体名称作为函数参数进行传递的时候属于值传递
#include <stdio.h>
struct Person {
int age;
char *name;
};
void change1(struct Person value);
void change2(struct Person *value);
int main(int argc, const char * argv[]) {
// 结构体名称作为参数传递,传递的是值
struct Person sp = {30, "lisi"};
printf("sp.age -->%i\n", sp.age); // sp.age -->30
change1(sp);
printf("sp.age -->%i\n", sp.age); // sp.age -->30
// 结构体地址做位参数传递
printf("sp.age -->%i\n", sp.age); // sp.age -->30
change2(&sp);
printf("sp.age -->%i\n", sp.age); // sp.age -->100
return 0;
}
void change2(struct Person *value) {
// 访问结构体指针内的属性使用 结构体指针名称->属性名进行访问
value->age = 100;
};
// value 接收结构体传参的时候,会将值复制一遍,跟基本类型传参类似
void change1(struct Person value) {
value.age = 100;
};