结构体的作用
在需要表示一些复杂信息时,使用单纯的数据类型很不方便。
比如:学生信息(学号,姓名,班级,电话,年龄);GPIO信息(GPIO输入输出、电平高低、是否中断等等)把这些信息组合在一起形成一种新的“数据类型”。
结构体的声明、结构体类型创建
结构体的声明可以分为三大类;①没有标签、②有标签、③有typedef
第一类
struct
{
char a;
short b;
int c;
} var1;
第二类
struct demo_tag
{
char a;
short b;
int c;
} var1;
第三类
typedef struct demo_tag
{
char a;
short b;
int c;
} demo_t;
typedef struct
{
char a;
short b;
int c;
} demo_t;
这三类都可以声明结构体,都是正确的。
区别在于:我们最终目的需要将一个变量定义为结构类类型;
采用第一种无标签的方法,每次需要写很多重复的东西,如下图
struct {
char a;
short b;
int c;
} var1;
struct {
char a;
short b;
int c;
} var2;
采用第二种有标签的方法,每次这么定义变量就行,如下图
(var1 是指var1变量是结构体类型的,不影响,相当于struct demo_tag var1)
struct demo_tag {
char a;
short b;
int c;
} var1;
struct demo_tag var2;
采用第三种使用tpyedef的方法,定义变量时就更简单了,如下图:
typedef struct demo_tag {
char a;
short b;
int c;
} demo_t;
demo_t var1,var2;
使用typedef时,代码中的demo_tag是没有用的,可以将demo_tag标签去掉,效果是一样的。
特别注意
1)结构体要包含 struct
2)最后要使用分号;
3)各成员之间也用分号隔开;;
结构体成员的访问
结构体成员依据结构体变量类型的不同,一般有2种访问方式,一种为直接访问,一种为间接访问。
直接访问应用于普通的结构体变量,间接访问应用于指向结构体变量的指针。直接访问使用结构体变量名.成员名,间接访问使用(*结构体指针名).成员名或者使用结构体指针名->成员名。相同的成员名称依靠不同的变量前缀区分。
struct SIMPLE
{
int a;
char b;
};
//声明结构体变量s1和指向结构体变量的指针s2
struct SIMPLE s1, *s2;
//给变量s1和s2的成员赋值,注意s1.a和s2->a并不是同一成员
s1.a = 5;
s1.b = 6;
s2->a = 3;
s2->b = 4;
计算结构体大小、结构体地址偏移
当我们用结构体保存GPIO寄存器时,每个寄存器地址偏移4字节,这时候就用到了结构的地址偏移。那么结构体的地址偏移和大小具体是怎么计算的呢?
两个原则:
1.结构体中成员的偏移量必须是该成员大小的整数倍;
2.结构体大小必须是所有成员大小的整数倍。
例1:
struct stu1
{
int i;
char c;
int j;
};
结构体变量中第一个成员的地址就是结构体变量的首地址。因此,第一个成员i的偏移量为0。
第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;
第三个成员j的偏移量是第二个成员的偏移量加上第二个成员的大小(4+1),其值为5。
单着并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,
使得第三个成员的偏移量变成8。所以总共结构体大小为12
例2:
struct stu2
{
int k;
short t;
};
成员k的偏移量为0;成员t的偏移量为4,都不需要调整。但计算出来的大小为6,
显然不是结构体大小的整数倍。因此,编译器会在成员t后面补上2个字节,使得结构体的大小变成8从而满足第二个要求。
例3:
struct stu3
{
char c1;
int i;
char c2;
}
结构体的大小为12
出现结构体嵌套时,怎么计算结构体大小?
1.展开后的结构体的第一个成员的偏移量(嵌套的结构体偏移量)应当是被展开的结构体中最大成员的整数倍;
2.结构体大小必须是所有成员大小的整数倍,这里计算的是展开后的成员,而不是将嵌套的结构体看作一个整体。
例如:
值传递、指针传递、引用传递
值传递
1.值传递的效率低,因为值传递需要对结构体做一次拷贝
2.值传递只是改变了形参,并未改变实参。
例如:输出的值不会发生任何改变
#include<stdio.h>
#include<string.h>
struct student
{
int num;
char name[20];
float score[3];
};
void change(struct student stu)
{
stu.score[0] = 100;
strcpy(stu.name, "jerry");
}
int main()
{
struct student stu;
stu.num = 12345;
strcpy(stu.name, "Tom");
stu.score[0] = 67.5;
stu.score[1] = 89;
stu.score[2] = 78.6;
change(stu);
printf(format, stu.num, stu.name, stu.score[0], stu.score[1],stu.score[2]);
printf("\n");
return 0;
}
指针传递
可以改变实参
<span style="font-family:Arial Black;font-size:12px;">#include<stdio.h>
#include<string.h>
#define format "%d\n%s\n%f\n%f\n%f\n"
struct student
{
int num;
char name[20];
float score[3];
};
void change(struct student* p)
{
p->score[0] = 100;
strcpy(p->name, "jerry");
}
int main()
{
struct student stu;
stu.num = 12345;
strcpy(stu.name, "Tom");
stu.score[0] = 67.5;
stu.score[1] = 89;
stu.score[2] = 78.6;
change(&stu);
printf(format, stu.num, stu.name, stu.score[0], stu.score[1],stu.score[2]);
printf("\n");
return 0;
}
指针传递 中,函数声明用 "*",实参传递用 “&”, 结构体成员赋值用 "->"
void change(struct student* stu); 声明函数时用*号!
change(&stu); 调用时用&
void change(struct student* p)
{
p->score[0] = 100;
strcpy(p->name, "jerry"); 赋值时用->
}
引用传递
首先声明,在标准C语言中没有引用传递,只有C++才有引用传递。这里简单的提一下引用传递。
引用传递和指针传递是一样的,都是传指针。只不过引用传递少了“解指针”的操作。可能速度能快一丢丢。
引用传递中,函数声明用 "&",实参传递直接传结构体名字就行