结构体声明
结构体的概念
结构体的概念:结构(结构体)是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
为什么要引入结构体呢?实际上是因为在生活中我们有许多复杂的事物,难以用一个int或者float等等一个简单的类型来描述清楚,例如一个学生,他应该有姓名,性别,身高,等等诸多因素,再比如一本书,有书名,作者,出版社,等等。所以在C语言中我们提供了自定义类型,有枚举,结构体,联合体等等,>我们今天只简单学一下结构体,其他的自定义类型会放到进阶里来学习。
结构的声明
假设我们要来描述一个学生,包括有姓名,性别,年龄,考试成绩这四个考虑因素,我们应该怎么来定义呢?
struct Stu
{
//结构体成员列表
char name[20];
char sex[10];
int age;
float score;
};//注意这里是有分号的
这样我们就定义了一个结构体类型,我们要描述一个学生,起的名称叫做Stu。
结构成员的类型
结构成员的类型是没有什么要求的,标量,数组,指针,其他结构体也可以(又开始套娃了对吧)。这一般提到C/C++就是都不得不提到C/C++的灵活性,这种灵活性是体现在方方面面的,所以要把C学精用好还是很有难度的。
结构体变量的定义和初始化
这里就是我们的重点了,我们创建了一个类型要先去定义变量然后初始化,最后去使用这个变量对吧,这是我们最终的目的。那么结构体变量如何创建呢?
这里其实有两种方式,我们来看一段代码:
struct Stu
{
char name;
char sex;
int age;
float score;
}s4,s5;//这里两种方式其实是等价的,
struct Stu s6;//定义的都是全局的结构体变量
int main()
{
struct Stu s1, s2, s3;//此处就是定义的局部的结构体变量了
return 0;
}
那么接下来就是我们的初始化了,怎么进行初始化呢?
struct Stu
{
char name[20];
char sex[10];
int age;
float score;
}s4 = { "如花","female",20,60.0 };
int main()
{
struct Stu s = { "zhangsan","male",18,95.5};
struct Stu s4 = { "如花","female",20,60.0 };
printf("%s %s %d %.1f\n", s.name, s.sex, s.age, s.score);
return 0;
}
还有一点值得一提的是,结构体里面是可以放结构体的,例如下面代码示例:
struct S
{
int a;
char b;
};
struct Stu
{
char name[20];
struct S;//结构体成员可以是结构体
int age;
};
int main()
{
//初始化也用大括号嵌套
struct Stu p = { "旺财",{28,'c'},18 };
printf("%s %d %c %d\n", p.name, p.a, p.b, p.age);
return 0;
}
看到这里,我想你也应该有点感觉了,结构体就是自己定义的一种类型,其实也不是什么高深的东西对不对。
结构体成员的访问
第一种访问方式
其实结构体成员的访问上面的代码已经示例了,就是通过’.'一个点这个操作符来访问的,它的操作数是两个,例如上面的使用:
printf("%s %s %d %.1f\n", s.name, s.sex, s.age, s.score);
printf("%s %d %c %d\n", p.name, p.a, p.b, p.age);
当我们使用 . 来访问的时候其实是有提示的,如图:
我们只需要选择我们想要使用的成员即可,需要注意的是,要与前面的打印格式一一对应。这个是个小细节,否则报错你可能看不懂找不到错误。
当然我们还可以用函数的方式来打印:
struct Stu
{
char name;
int age;
};
void Print(struct Stu p1)
{
printf("%s %d\n", p1.name, p1.age);
}
int main()
{
struct Stu p = { "如花",18 };
Print(p);//传值调用
return 0;
}
第二种访问方式
除了上面的访问方式呢,还有另外一种,只能用于结构体指针,是通过->一个箭头来访问,代码示例如下:
#include<stdio.h>
struct S
{
int a;
char b[20];
};
struct Stu
{
char name[20];
struct S;
int age;
};
void Print(struct Stu* sp)
{
printf("%s %d %d %s\n", (*sp).b,(*sp).a,(*sp).age,(*sp.).name);//比较啰嗦的写法
printf("%s %d %d %s\n", sp->b, sp->a, sp->age, sp->name);//使用箭头直接指向对象
}
int main()
{
struct Stu p = { "旺财",{28,'c'},18 };
Print(&p);
return 0;
}
总结结构体成员两种访问格式,1.结构体变量.成员,2.结构体指针->成员。
结构体传参
其实上面我们连带着把结构体传参也已经涉及到了,但是我们这里再来总结一下:
#include<stdio.h>
struct s
{
float score;
};
struct Stu
{
char name[20];
int age;
struct s;
};
void Print1(struct Stu p1)
{
printf("%s %d %.1f\n", p1.name, p1.age, p1.score);
}
void Print2(struct Stu* p2)
{
printf("%s %d %.1f\n", p2->name, p2->age, p2->score);
}
int main()
{
struct Stu p = { "李华",18,{90.5} };
Print1(p);//传值调用
Print2(&p);//传址调用
return 0;
}
你觉得上面Print1和Print2哪一种传参方式好一点,传值和传址两种方式,其实这道题看似两种方法都可以,但实际上是有标准答案的,一定是第二种方式,传址调用,为什么呢?我们就要回到函数栈帧那块的内容。
函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
我们用通俗的解释一下就是,我们都知道,函数的形参是一份临时拷贝,当我们传的参数过于多或者大的时候,是不是要浪费很多空间啊,而如果是传一个首元素地址过去,是不是就没什么压力,地址大小一定是4或8个字节嘛。
结论:函数传参的时候,要传地址来使用。
好啦,关于结构体这块内容就到这里了,只要理解了结构体是个什么东西,会使用即可。