0
点赞
收藏
分享

微信扫一扫

MemFire解决方案-物联网数据平台解决方案

前言

目录

前言

1----结构体

1-1结构体的声明

1-2结构体变量的创建和初始化

1-3结构体的特殊声明

1-4 结构体的自引用

1-5计算结构的大小

练习1——(规则1,2,3)

练习2——(规则4)

1-6内存对齐的原因

1-7修改默认对齐数

1-6结构体传参

1-7位段

1-7-1认识位段

1-7-2位段的内存分配

1-7-3位段的跨平台问题

1-7-4位段的使用的注意事项

1-7-5位段的拓展

2----联合体

2-1联合体类型的声明

2-2联合体的特点

2-2-1对比结构体和联合体的内存发布情况

2-3联合体的大小计算

2-3-1实际运用

2-4联合体的经典练习

2-4-1 方法1--联合体

2-4-2方法2--字节

3----结构体与联合体区别(重点)

        3-1. 内存利用方面    

        3-2. 成员访问方面       

        3-3. 用途方面         

        3-4  总结

4----枚举

4-1枚举的声明

4-2枚举类型的优点

4-3枚举类型的使用

5----自定义类型的总结

6----警告的总结

7----编程提示的总结


1----结构体

1-1结构体的声明

结构体的表示形式

假如要描述一个学生,就可以使用结构体类型~~

//结构体的声明
struct students 
{
	char name[20];//姓名
	int age;//年龄
	float score;//成绩
	char id[20];//身份证号
};//注意:分号不能丢

注意:分号不能丢

在声明结构体时可以使用typedef创建一种新的类型

比如:

typedef struct students
{
	char name[20];//姓名
	int age;//年龄
	float score;//成绩
	char id[20];//身份证号
}stu;//分号不能丢

这个技巧和声明一个结构标签的效果相同,区别于stu现在是一个类型名但不是结构标签~~

1-2结构体变量的创建和初始化

例如:初始化学生的信息,一起看看代码吧~~

//结构体的创建和初始化
#include<stdio.h>
struct students
{
	char name[20];//姓名
	int age;//年龄
	float score;//成绩
	char id[20];//身份证号
};//分号不能丢
int main()
{
	//1-按结构体成员的顺序初始化
	//struct students s = { "张三",18,90.5f,"123456789"};
	//2-按指定的顺序初始化
	struct students s = { .name="张三",.age=18,.score=90.5f,.id="123456789" };
	printf("name:%s\n", s.name);
	printf("age:%d\n", s.age);
	printf("score:%f\n", s.score);
	printf("id:%s\n", s.id);

	return 0;
}

1-3结构体的特殊声明

在声明结构的时候,可以不完全的声明。(缺少标签)——匿名结构体

比如下面的结构体就是一个匿名结构体:


struct
{
	char b;
	int a;
	float c;
}x;

但匿名结构体也存在问题

例如:

//匿名结构体只能使用一次
struct
{
	char b;
	int a;
	float c;
}x;
struct
{
	char b;
	int a;
	float c;
}*ps;
#include<stdio.h>
int main()
{
	ps = &x;
	return 0;
}

输出结果:

这段代码与匿名结构体有关

上面的结构在声明的时候省略了结构体的标签。

那么

分析:

编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。

匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次!!

对匿名结构体的改进

可以使用typedef重命名匿名结构体,这样就可以消除匿名结构体只能使用一次的缺陷!!

当这个结构体只使用一次是可以考虑使用匿名结构体,否则不要考虑!!

当然匿名结构体也可以重命名~~

//改善
typedef struct 
{
	char b;
	int a;
	float c;
}stu;//匿名结构体类型也可以重新命名
#include<stdio.h>
int main()
{
	stu s;
	return 0;
}

当然匿名结构体也可以进行初始化~~

​
struct 
{
	int a;
	char b;
	double c;
}s = {18,'A',3.14};//在这里创建变量,也可以初始化,没有标签名
#include<stdio.h>
int main()
{
	printf("%c %d %lf", s.b, s.a, s.c);
	return 0;
}

​

输出结果:

1-4 结构体的自引用

在结构体中包含一个类型为该结构体本身的成员可以吗?

比如:定义一个链表的节点,其中节点的结构是:(数据域+指针域)/(数据和地址

 struct Node {
	int data;
	struct Node next;
};

那么试试计算链表节点这个结构体的大小吧~~

其实这样的引用方式是错误的,因为一个结构体中再包含一个同类型的结构体变量,这样结构体变量的大小就会无穷的大,这是错误的!!

那么看看正确的自引用方式吧~~

 struct Node {
	int data;//数据
	struct Node* next;//指针
};

在结构体自引用的过程中,使用typedef对匿名结构体重命名,也容易产生问题,看看下面的代码可行吗?

typedef struct {
	int data;//数据
	Node* next;//指针
}Node;

其实这样是不行的,因为Node是对前面的匿名结构体类型的重命名产生的,但在匿名结构体内部提前使用Node类型来创建成员变量,这是不行的!!

解决方案:定义结构体不要使用匿名结构体!!

实例代码如下:

typedef struct Node {
	int data;//数据
	struct Node* next;//指针
}Node;

关于结构体的自引用,有两种写法。完整代码如下~~

//结构体的自引用
//匿名的结构体类型是不能实现这种结构体的自引用效果的
#include<stdio.h>
typedef struct Node {
	int data;//数据
	struct Node* next;//指针
}Node;
// 写法1
 struct Node {
	int data;//数据
	struct Node* next;//指针
};//写法2
int main()
{
	struct Node Node1;
	Node node1;
	return 0;
}

1-5计算结构的大小

想知道结构体的大小我们得学习结构体内存对齐~~

1-5-1对齐规则

首先我们得掌握结构体的对齐规则

比如:偏移量是4的倍数,地址便是4的倍数~

试着做这两道练习,检验你是否真的会计算结构体大小~~

注意:VS对齐数一般设为2的整数倍

练习1——(规则1,2,3)

//计算结构体的大小
//内存对齐
#include<stdio.h>
struct S2
{
	char c1;//1 8 1
	char c2;//1 8 1
	int i;//4 8 4
};
int main()
{
	printf("%zd\n", sizeof(struct S2));

return 0;
}

输出结果:

解析:

练习2——(规则4)

#include<stdio.h>
struct S3
{
	double d;//8 8 8
	char c;//1 8 1   所有最大对齐数中最大的是8,那么结构体的大小就是8的倍数
	int i;//4 8 4
	float e;//4 8 4
};//大小为24
struct S4
{
	char c1;//1 8 1
	struct S3 s3;//看s3里的最大对齐数-8
	double d;//8 8 8
};
int main()
{	
printf("%zd\n", sizeof(struct S4));//返回size_t
return 0;
}

输出结果:

解析:

S3结构体大小的计算

S4结构体大小的计算

1-6内存对齐的原因

1、平台原因

2、性能原因

在设计结构体是要满足内存对齐,又要节省空间

可以让占用空间小的成员,尽量集中在一起~~

下面这段代码就可以说明这个方法~~

尽管类型成员相同,但它们所占的空间大小有所不同
struct S1
{
	char c1;//1
	int i;//4
	char c2;//1
};
struct S2
{
	char c1;//1
	char c2;//1
	int i;//4
};
int main()
{
	printf("%zd\n", sizeof(struct S1));//12
	printf("%zd\n", sizeof(struct S2));//8

	return 0;
}

输出结果

1-7修改默认对齐数

#include<stdio.h>
#pragma pack(1)
//#pragma pack(2)
//设置默认对齐数
struct S1
{
	char c1;//1 1 1 1 2 1
	int i;//1 4 1   2 4 2
	char c2;//1 1 1 1 2 1
	//对齐数为1时,每个成员对齐到数字1上,说明没有对齐
	//那么连续存放就OK,最终结构体的大小只要是1的倍数就OK
};
#pragma pack()//取消设置的对齐数,还原为默认的
int main()
{
	printf("%zd\n", sizeof(struct S1));//3
	return 0;
}

1-6结构体传参

#include<stdio.h>
//struct book {
//	char name[20];
//	int price;
//}s = {"简爱",100};//初始化写法1
struct book {
	char name[20];
	int price;
};
struct book s={ "简爱",1000 };//初始化写法2
void Print(struct book* ps)
{
	printf("name->%d\n", ps->price);
}
int main()
{
	Print(&s);
	return 0;
}
//结构体传参的时候要传结构体的地址!!

输出结果:

结构体传参的时候要传结构体的地址的原因:

1-7位段

1-7-1认识位段

位段是专门用来节省内存的。

位段声明和结构是类似的,但有所差异~

位段的声明

struct A
{
	int _a ;
	int _b ;//4个字节占32个比特位
	int _c ;
	int _d ;
	int _e ;
};

那么位段的大小怎么计算呢?

例如

位段——“位”:二进制位
struct A
{
	int _a : 2;//_a只占2个比特位
	int _b : 5;
	int _c : 10;
	int _d : 30;
	int _e : 31;
};

A就是一个位段,那么A的大小是多少呢?

//位段——位:二进制位
struct A
{
	int _a : 2;//_a只占2个比特位
	int _b : 5;
	int _c : 10;
	int _d : 30;
	int _e : 31;
};
#include<stdio.h>
int main()
{
	printf("%zd\n", sizeof(struct A));//12
	return 0;
}

输出结果

为什么是12呢?想知道这个得知道位段的内存分配~~

1-7-2位段的内存分配

注重可移植性的程序应该避免使用位段。由于下面这些与实现有关的依赖性,位段在不同的系
统中可能有不同的结果。 

就这上面的代码,解释上这段吧~~

那么现在可以解释为什么A的大小是12字节了。

图解:

为了再次认识位段的内存分配,看看下面这段代码吧~~

struct S
{
 char a:3;
 char b:4;
 char c:5;
 char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
//这个位段的空间是如何开辟的呢?

解析:

例如:存数字0,1,2,3
00-0,01-1,10-2,11-2,两个二进制位就够了,但有30个比特位浪费了
此时就可以使用位段——在一定程度节省内存空间
C语言没有规定标准,剩余空间不够时,是浪费还是继续存放
这就取决于编译器
VS上数据从右往左存,剩余空间不够时,是浪费
#include<stdio.h>
struct S
{
	char a : 3;//010
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = { 0 };
	s.a = 10;//1010发生截断,存的是010
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

图解

调试过程

1-7-3位段的跨平台问题

1-7-4位段的使用的注意事项

//位段的使用
//使用位段的结构体类型中的成员类型应该相同
struct A
{
	int _a : 2;//_a只占2个比特位
	int _b : 5;
	int _c : 10;
	int _d : 30;
	int _e : 31;
};
#include<stdio.h>
int main()
{
	struct A sa = { 0 };
	int b = 0;
	scanf("%d", & b);
	sa._b = b;
	printf("%zd", sizeof(struct A));
	return 0;
}

1-7-5位段的拓展


2----联合体

2-1联合体类型的声明

//联合体类型的声明
#include<stdio.h>
union un {
	char a;
	int b;
};
int main()
{
	union un u = { 0 };//联合体变量的定义
	//计算联合体变量的大小
	printf("%zd\n", sizeof(u));
	return 0;
}

输出结果:

为什么输出结果是4呢?

那要看联合体类型的特点呢~~

2-2联合体的特点

看看这两段代码,相信你会对此有深入的理解。

第一段代码

​
#include <stdio.h>
//联合体的声明
union Un
{
 char c;
 int i;
};
int main()
{
 //创建联合体变量
 union Un un = {0};
 // 它们的地址大小一样?
 printf("%p\n", &(un.i));
 printf("%p\n", &(un.c));
 printf("%p\n", &un);
 return 0;
}

​

输出结果:

再看看第二段代码

#include <stdio.h>
union Un//联合体的声明
{
	char c;
	int i;
};
int main()
{

	union Un un = { 0 };//联合体变量的创建
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
	return 0;
}

输出结果:

从输出结果可以看出,第一段代码输出的地址都一样,而从第二段代码输出结果可以分析,将i的第四个字节的内容修改为55了。

因此可以画出联合体(un)的内存发布图

2-2-1对比结构体和联合体的内存发布情况

我们再对比一下相同成员的结构体和联合体的内存发布情况。

结构体

struct S
{
 char c;
 int i;
};

内存分布情况:

联合体:

union un
{
 char c;
 int i;
};

内存发布情况:

2-3联合体的大小计算

试着计算下面两个联合体的大小吧~~

// 计算联合体的大小
#include<stdio.h>
union Un1
{
	char c[5];//1 8 1
	int i;//4 8 4
};
union Un2
{
	short c[7];//2 8 2
	int i;//4 8 4
};
int main()
{
	union Un1 u1 = { 0 };
	union Un2 u2 = { 0 };

	printf("%zd\n", sizeof(u1));
	printf("%zd\n", sizeof(u2));

	return 0;
}

输出结果:

解析

联合体Un1的内存发布

联合体Un2的内存发布

2-3-1实际运用

比如有一个礼物兑换单,礼物兑换单中三个商品:书,杯子,衬衫。

每一种商品都有库存量,价格,商品类型和商品类型相关的其他信息

如果将这些信息简单的一 一罗列在一个结构体中,用起来很方便,但这样使得结构体的大小偏大,比较浪费内存,因为对于兑换单中的商品来说,只有部分属性信息是常用的,比如商品为书,就不需要design,colour,size……

所以可以把公共属性单独写出来,剩余属于各种商品本身的属性使用联合体联合起来,这样在一定程度上节省了内存~~

struct gift_list 
{
	//公共属性
	int stock_number;//库存量
	double price;//价格
	int type;//商品类型
	//每个商品具有的自己的属性
	union {
		struct {
			char book_name[20];//书名
			char author[20];//作者
			int page;//页数
		}book;
		struct {
			char design[20];//设计
		}cup;
		struct  {
			char design[20];//设计
			char colour[10];//颜色
			char size[10];//尺寸
		}shirt;
	};
};

2-4联合体的经典练习

写一个小程序判断,你的机器是大端模式还是小端模式

在写程序之前,先来回顾一下大端和小端模式

2-4-1 方法1--联合体

练习判断一个机器是大端还是小端

根据联合体的内存发布特点可以轻松解决

 写法1
#include<stdio.h>
int check_sys()
{
	union {
		int a;
		char b;
	}sa;
	sa.a = 1;
	return sa.b;
}
int main()
{
	if (check_sys())
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}

	return 0;
}

2-4-2方法2--字节

练习判断一个机器是大端还是小端

判断一个字节存储的是1还是0

//写法2
​
#include<stdio.h>
int check_sys()
{
	int i = 1;
	return (*(char*)(&i));
}
int main()
{
	if (check_sys())
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}

	return 0;
}

​

本电脑的输出结果:


3----结构体与联合体区别(重点)

        C语言中结构体(struct)与联合体(union)是两种不同的数据结构,它们的主要区别在于内存利用、成员访问和用途。具体分析如下:

        3-1. 内存利用方面    

        3-2. 成员访问方面       

        3-3. 用途方面         

        3-4  总结


4----枚举

4-1枚举的声明

比如:性别:男、女,可以一 一列举

三原色:红、绿、蓝,也可以一 一列举~

这些数据的表示就可以使用枚举!!

比如三原色使用枚举类型表示:

//枚举类型的声明
enum Colour {
	RED ,
	GREEN ,
	BLUE ,
};

比如:

enum Colour{
	RED = 2,
	GREEN = 4,
	BLUE = 6,
};

4-2枚举类型的优点

之前我们学习了#define定义常量,那么枚举类型与之有什么不同呢?

4-3枚举类型的使用

enum Colour{
	RED = 2,
	GREEN = 4,
	BLUE = 6,
};
enum Colour clr = GREEN;//使用枚举常量给枚举变量赋值

C语言中可以拿整数给枚举变量赋值,但在C++中不行!!


5----自定义类型的总结


6----警告的总结


7----编程提示的总结

制作不易,老铁们三连吧,别下次一定了!!

举报

相关推荐

0 条评论