嵌入式全栈开发学习笔记---C语言(结构/联合/枚举)

目录

结构体

结构体的声明

定义结构体变量

访问结构体成员进行初始化

通过结构体变量名访问结构体成员

结构体指针

结构体指针的定义

通过结构体指针访问结构体成员

结构体数组

结构体数组的定义

遍历结构体数组

结构体的长度(笔试重点)

联合体

联合体的定义

联合体的长度

如果来判断设备的字节序?

如何把大端数据转换成小端数据?

枚举

枚举的定义


上一篇复习了最后6道编程题,这一节开始复习结构体、联合体和枚举的内容。

结构体

为什么要学习结构体?

我们以往存储数据都是用数组来存储的,但是数组有个缺点就是存储的数据的类型必须是一样的。如果我们想要存储一个人的信息,比如,身高、年龄等,身高是浮点型,年龄是整型,那这种数据用数组来存储是不行的,因此我们需要学习结构体。

结构体的声明

struct的语法格式如下:

struct  结构体名 {成员表列}; //分号不能丢

比如:

struct student

{

        int num;

        char name[20];

        float score;

        char addr[30];

} ; //注意:这个分号不能丢

注:struct是一个关键字,结构体内部的各变量为结构体成员。

结构体声明后之后可以定义结构体变量:

定义结构体变量

例如:struct student s1;

定义结构体变量初始化

和其它类型变量一样,对结构体变量可以在定义时指定初始值。

例如:struct student s2={jack,1,m};

访问结构体成员进行初始化

通过结构体变量名访问结构体成员

我们前面的s1没有初始化,如果你想要让它初始化时,可以通过“结构体变量名.成员名”来访问结构体中的成员,例如:s1.id=2; s1.sex=f;

注意:如果要通过结构体变量名来访问数组成员进行初始化,不能直接写成s1.name=tom;//这样写等价于char name[32];name=aaa;由于name数组名是个地址常量,因此不能被修改。

可以这样写:strcpy(s1.name,tom);

也可以在结构体声明时直接初始化:

struct Books

{

   char  title[50];

   char  author[50];

   char  subject[100];

   int   book_id;

} book = {"C 语言", "RUNOOB", "编程语言", 123456};

如果嫌结构体的声明部分太长,可以将结构体声明改个名字:

例如:

typedef struct Books

{

   char  title[50];

   char  author[50];

   char  subject[100];

   int   book_id;

}stu ;

这样我们就把

struct Books

{

   char  title[50];

   char  author[50];

   char  subject[100];

   int   book_id;

};

这么一大串改成了stu

通过结构体变量名输出结构体成员,只能一位一位输出,例如:printf(%s %d %c\n,s1.name, s2.id, s1.sex);

结构体指针

结构体指针的定义

结构体指针应该指向结构体变量,我们要先申请一块内存给结构体变量,然后再让结构体指针指向这块内存。

stu *ps=(stu*)malloc(sizeof(stu)*1);

通过结构体指针访问结构体成员

通过“结构体指针变量名->成员名”来访问结构体成员并初始化

ps->id=3;

ps->sex=m;

注意:如果是访问数组成员,应写成strcpy(ps->name,boy);

如果要通过指针输出结构体成员,应该写成例如:printf(%s %d %c\n,ps->name, ps->id, ps->sex);

结构体数组

结构体数组的定义

数组中的每个元素都是一个结构体

例如:

stu s3[5]={{aa,4,f},{bb,5,f}};

遍历结构体数组

int i;

for(i=0;i<5;i++)

{

        printf(“%s %d %c\n,s3[i].name,s3[i].id,s3[i].sex);

}

结构体的长度(笔试重点)

计算结构体的长度有两个原则:

  1. 结构体的总长度一定是最长成员的数据类型的整数倍;
  2. 每个成员的偏移量一定是该成员长度的整数倍;

例如:

struct test

{

int a;

int b;

char c;

char d;

} ;

这样运行出来的结果是12

因为该结构体有4个成员,要保证结构体的总长度一定是最长成员的数据类型的整数倍,因此要在后面补两个字节。

如果我们将该结构体成员调整一下顺序,

例如:

struct test

{

int a;

char c;

int b;

char d;

} ;

这样运行出来的结果是16

因为char后面是int,为了保证int偏移量是自己的整数倍,因此char后面要空出三个字节,然后又由于int是最长成员,为了保证int总长度是int的整数倍,因此在后面补3个字节,所以总长度是16

但是笔试的时候会有更加复杂的情况,比如结构体中嵌套结构体

例如:

struct test 2

{

int z;

struct test t;

char e;

};

注意:不要以为总长度是struct test t长度的整数倍,我们计算的时候还是要把struct test t拆开,最长成员还是int。计算的时候要保证struct test t在内存中的完整性,不能拆开。

这样运行的结果是24

联合体

为什么要学习联合体?

结构体有个缺点就是比较占内存,因为我们每次使用结构体时要给里面的每个成员都开辟空间,但是有时候我们只需要用到里面的某个成员,而不是全部,我们只想为我们使用到的成员申请空间,但是其他成员又不能直接删掉,因为我们有时候还会使用到其他成员,所以怎么办呢?这个时候联合体就派上了用场。

联合体又叫共用体。是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。联合体可以带有多个成员,但是任何时候只能有一个成员带有值。联合体提供了一种使用相同的内存位置的有效方式。

联合体的定义

定义联合体需要用到union关键字。union 语句定义了一个新的数据类型,带有多个成员。

union 语句的格式如下:

union 共用体名

{          

        成员表列

};

联合体的长度

例如:

union test

{

        int a;

        int b;

        char c;

};

它的总长度是4

联合体的特点:

  1. 联合体的所有成员共享同一块内存空间;
  2. 联合体大小:只为最长成员分配空间;

如果我们给a初始化为100,test.a=100;

由于内存是同一个空间,我们访问a,即是访问b,因此当我们打印b的时候,结果也是100,如果访问char c情况就有可能不一样,因为我们不知道c是在4个字节的哪一端,取决于电脑的字节序。

笔试的时候经常用联合体来判断设备的字节序。(这个我们在复习一维数组的时候就已经提到过了)

小端字节序:低字节存放在低地址,高字节存放在高地址。

大端字节序:高字节存放在低地址,低字节存放在高地址。

注意:数组无疑是:数组元素的地址是从低地址到高地址,比如a[0]存放在低地址,a[4]存放在高地址。

如果来判断设备的字节序?

示例:

假设联合体:

union test

{

        char ch[2];

        short val;

};

我们让val=0x0102;这个是16进制,里面有两个字节,分别为0000 0001(高字节)和0000 0010(低字节),我们由于ch和val共享一个空间,那么我们只要判断0000 0001是放在了ch[0](低地址)还是放在了ch[1](高地址)就能知道设备是小端字节序还是大端字节序了。

参考代码:

#include <stdio.h>

union test
{
	char ch[2];//两个字节
	short val;//short是两个字节
};

int main()
{
	//定义联合体变量
	union test t;
	t.val=0x0102;
	if(t.ch[0]==1&&t.ch[1]==2)
	{
		printf("big\n");
	}
	else if(t.ch[0]==2&&t.ch[1]==1)
	{
		printf("small\n");
	}
	return 0;	
}

运行结果:

该设备为小端字节序,一般来说电脑或者我们使用的开发板一般都是小端字节序。

如何把大端数据转换成小端数据?

我们只需要将高字节和低字节调换位置即可

示例:

假设int num=1; 0000 0000 0000 0000 0000 0000 0000 0001,这个1属于低字节,现在要求将它挪到高字节。

提示:将低字节取出来通过位移运算移动到高字节。

参考代码:

#include <stdio.h>

int main()
{
	int num=1;
	printf("%d\n", ((num & 0x000000ff)<<24)|(num &0x0000ff00<<8)|(num &0x00ff0000>>8)|(num &0xff000000>>16));
	return 0;
	
}

运行结果:

用计算器计算的结果也是16777216

枚举

枚举的关键字:enum

枚举和宏定义有点类似。但是有些情况宏定义显得有点繁琐,比如给每个星期的英文缩写重新定义一个数字, 那么我们需要写:

#define MON 1

#define TUE 2

#define WED 3

......

一个星期7天,那我们重复宏定义7次,这样就显得有点繁琐了,因此我们需要用到枚举。

枚举的定义

enum weekday //这个weekday在这里可以不用也写

{

        sun,mon,tue,wed,thu,fri,sat

};

虽然枚举中我们并没有给sun,mon,tue,wed,thu,fri,sat定义0,1,2,3,....但是编译器会默认sun是0,mon是1,tue是2....这样就可以申请繁琐的定义操作。

当然如果你不想要枚举默认,那么你也可以自己指定,比如

enum

{

        sun,mon=11,tue,wed,thu,fri,sat

};

当我们这样讲mon定义成11时,那么后面的tue也就跟着变成12了,依次递增。

下节学习内存管理!

以上就是这篇内容,如想了解更多,欢迎订阅本专栏!

如有问题可评论区或者私信留言,如果想要进交流群请私信!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vera工程师养成记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值