目录
一. 结构体的声明
1.结构的基础知识
结构是一些值的集合,这些值称为结构变量,结构的每个成员可以是不同类型的变量
数组:一组相同类型元素的集合
结构体:其实是一组不一定相同类型元素的集合
生活中的描述:书:书名+作者+出版社+定价+书号.....
这些复杂的对象,不能通过内置类型直接描述,就有了结构体来描述这种复杂类型
2.结构的声明
//描述一个学生
//名字+年龄+性别
//声明结构体类型
struct Stu
{
//成员变量,用来描述结构体对象的相关属性
char name[20];
int age;
char sex[5];//男 女 保密\0
}s2,s3,s4;//s2,s3,s4就是结构体变量 - 全局变量
int main()
{
struct Stu s1;//局部变量
return 0;
}
类型相当于生活中的房子图纸,而变量相当于房子
当然创建s2,s3和s4还可以这么写
typedef struct Stu
{
char name[20];
int age;
char sex[5];
}Stu;//对这个结构体类型重新起了一个名字叫Stu
int main()
{
struct Stu s1;//局部变量
Stu s2;//用Stu来创建s2的时候跟s1的创建是一个道理
//在C语言中,如果没有对结构体类型typedef,struct关键字不能省略
return 0;
}
3.结构体成员的类型
可以是标量,指针,数组,甚至是其他结构体
struct S
{
int a;
char arr[5];
int* p;
};
struct B
{
char ch[10];
struct S s;
double d;
};
二. 结构体初始化
struct S
{
int a;
char arr[5];
int* p;
}s1;//第一种变量创建方式
struct S s2;//第二种变量创建方式
struct B
{
char ch[10];
struct S s;
double d;
};
int main()
{
struct S s3;//第三种变量创建方式
return 0;
}
//第四种变量创建方式就是typedef
初始化和数组一样,都是用大括号包围起来
struct S
{
int a;
char arr[5];
int* p;
}s1 = {100, "bit", NULL};//要根据成员顺序来
struct S s2 = { .arr = "abc", .p = NULL, .a = 1 };//这样创建不用按照顺序,因为顺序你自己可以定
打印出来效果怎么样呢
三. 结构体成员访问
如何用struct B 来创建变量呢?这个过程比较复杂,我直接上代码和结果吧
现在来定义一个Stu的结构体,并实现可以调整学生信息的set_stu()和打印学生信息的print()方法
#include <string.h>//字符串操作需要加这一句
struct Stu
{
char name[20];
int age;
};
void set_stu(struct Stu t)
{
t.age = 20;
//t.name = "张三";//error 因为这里的“张三”是地址,这就不对了,我们应该把张三放到name申请的空间里面
strcpy(t.name, "张三");//字符串拷贝
}
void print_stu(struct Stu t)
{
printf("%s %d\n", t.name, t.age);
}
int main()
{
struct Stu s = {0};
set_stu(s);//设置s的值
print_stu(s);
return 0;
}
这代码看起来没毛病,但是打印一下
欸为什么是0呢?
因为这里set_stu(struct Stu t)的t是set_stu(s)里的s 的临时拷贝,在t里面设置age和name,设置的挺开心,回头一看因为临时拷贝,t的改变不影响s,所以print打印出来的啥也没有只有一个0。
所以我们要给set_stu里面传一个地址,然后print和set_stu函数里面传一个指针,根据地址找地址肯定是没错的
修改后的代码
struct Stu
{
char name[20];
int age;
};
//方式1
void set_stu(struct Stu* ps)
{
(*ps).age = 20;
//t.name = "张三";//err
strcpy((*ps).name, "张三");//字符串拷贝
}
//方式2
//void set_stu(struct Stu* ps)
//{
// ps->age = 20;//结构体指针->结构体成员
// //t.name = "张三";//err
// strcpy(ps->name, "张三");//字符串拷贝
//}
void print_stu(struct Stu t)
{
printf("%s %d\n", t.name, t.age);
}
int main()
{
struct Stu s = { 0 };
set_stu(&s);
print_stu(s);
return 0;
}
打印结果
四. 结构体传参
观察下面的代码
struct S
{
int data[1000];
int num;
};
struct S s = { {1,2,3,4},1000 };
void print1(struct S s)
{
printf("%d\n", s.num);
}
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s);//传结构体
print2(&s);//传地址
return 0;
}
一般我们选择print2函数来进行打印,因为实参进行传值调用的时候,形参还要再拷贝一份
如果用print1函数,当实参结构体s特别大,再进行拷贝,又创建了一个特别大的空间,空间浪费严重。
我们知道,传参的过程,参数需要压栈,一旦参数的空间特别大的时候,压栈的开销会很大,既浪费空间也浪费时间。
而地址就不会发生这样的情况,地址的大小是4或8个字节,每次压栈的时候,系统的开销不是很大。
建议:传参的时候,建议传结构体的地址