C语言杂项2

1、关于main函数

为什么mian函数必须要返回一个int?
在裸机程序里面返回值确实没有什么用途;如果使用了操作系统 就有意义了。
还以一个原因:可移植性。C语言标准是不承认void main的,有的编译器可能编译不通过。

STM32上,用cubeMX生成的代码确实是int main,但是程序里并没有return!!!原因是编译器会自动给main函数最后补一个return。

2、重复定义

.h文件中只做宏定义函数变量声明类型定义,而永远不要放函数实现变量定义,把这些都放到.c文件中。

3、结构体

3.1、结构体赋值

/* 结构体的赋值 */
struct x
{
	int a;
	int b;
};
struct x A;
A.a = 1;
A.b = 2;

/* C99 */
struct x
{
	int a;
	int b;
};
struct x A = {1,2}
/*************************************/
struct x
{
	int a;
	int b;
};
struct x A = 
{
	.a = 1,
	.b = 2,
};
/*************************************/
struct x B;
B = A;
/*************************************/
memcpy(&B,&A,sizeof(A));

3.2、结构体开头前128个字节访问更快

如果数据成员相对于结构体开头偏移小于128字节,则该偏移量可以使用8位有符号的数字表示。
也就是可以通过编码为8位有符号数字的偏移量来访问,或者根本不需要偏移量,编译器有可能会把前128字节的内容进行特殊处理(优化)
所以在定义结构体成员的时候把需要经常访问的成员放到结构体前面,这里程序执行效率会更高。

3.3、结构体大小

对齐规则:

  • 第一个成员与结构体变量偏移量为0的地址处。其他成员变量都要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器的默认对齐其数(STM32 ARMCC 和 GCC下的默认对其数为4)与该成员字节大小的较小值。

  • 结构体总大小为最大对齐数(成员变量中类型最长的)的整数倍。

struct y{
	uint8_t a; //起始地址为 0
	uint32_t b;	//对其数为4,起始地址为 4,导致a后面空了3个字节。
	uint8_t c; //起始地址为 8,本来可以只用9个字节,但是要是4的整数被,所有实际c后面也空了3个字节。
};
// sizeof(y) = 12
struct y{
	uint8_t a;//起始地址为0
	uint8_t c;//对齐数为1,起始地址为1的倍数,起始地址为1
	uint32_t b;	//对其数为4,起始地址为4,导致c后面空2个字节
};
// sizeof(y) = 8
struct y{
	uint8_t a;//起始地址为0
	uint8_t c;//对齐数为1,起始地址为1的倍数,起始地址为1
	uint8_t e;//对齐数为1,起始地址为1的倍数,起始地址为2
	uint32_t b;	//对其数为4,起始地址为4,导致e后面空1个字节
};
// sizeof(y) = 8
struct y{
	uint8_t a;//起始地址为0
	uint8_t c;//对齐数为1,起始地址为1的倍数,起始地址为1
	uint8_t e;//对齐数为1,起始地址为1的倍数,起始地址为2
	uint8_t f;//对齐数为1,起始地址为1的倍数,起始地址为3
	uint32_t b;	//对齐数为4,起始地址为4的倍数,起始地址为3
};
// sizeof(y) = 8
struct y{
	uint8_t a;//起始地址为0
	uint32_t c;//对齐数为4,起始地址为4的倍数,起始地址为4,导致a后面空3个字节
	uint16_t e;//对齐数为2,起始地址为2的倍数,起始地址为8
	uint32_t b;	//对齐数为4,起始地址为4的倍数,起始地址为12,导致e后面空2个字节
};
// sizeof(y) = 16
struct y{
	uint8_t a;//起始地址为0
	uint16_t c;//对齐数为2,起始地址为2的倍数,起始地址为2,导致a后面空1个字节
	uint32_t e;//对齐数为4,起始地址为4的倍数
	uint8_t b;	//对齐数为1,起始地址为1的倍数,起始地址为8,本来可以只用9个字节,但是要是4的整数被,所有实际c后面也空了3个字节。
};
// sizeof(y) = 12

3.4、位域

  • 把一个寄存器定义成一个结构体,通过位域的方法进行寄存器的配置操作将非常的方便。
  • 在位域定义中同一个类型域中永远都是先定义低地址,然后地址顺序进行增长。
  • 可以不定义位域的名字,这样它就是只是一个占位而已。
  • 位域在计算的时候自动升级为整形。
typedef struct
{
	uint32_t regs:24;
	uint32_t :8;  //占位,高8位保留未使用。
}data_reg;

4、联合

通过联合体判断大小端

typedef union
{
	uint32_t a;
	uint8_t b[4];
}u_x;
u_x x;
x.a = 0x12345678;
printf("b[0]= 0x%x,b[1]= 0x%x,b[2]= 0x%x,b[3]= 0x%x.",
		x.b[0],
        x.b[1],
		x.b[2],
		x.b[3]);

// b[0] = 0x78,b[1] = 0x56,b[2] = 0x34,b[0] = 0x12.
//低地址低字节 小端

联合体最经典的应用就是在通讯过程中对接收数据进行解析,以及对发送数据进行封装。

typedef union
{
	struct{
		uint8_t header;
		uint8_t datatype;
		uint16_t len;
		uint16_t data_bu[1];//占位
	}frame;
	uint8_t buf[100];
}data_frame;

data_frame x;
//通过 x.buf接收数据
//x.frame.header 解析数据

联合就是让同一块内存拥有不同的类型,通过其中的任何一种类型来访问这块内存。

//浮点数由三部分组成:符号位S,指数部分E(称为阶码)和尾数部分M
typedef union
{
	float a;
	uint8_t bytes[4];
} data_uni;

data_uni x = {.a = 1.0};
data_uni *p = &x;
p->bytes[3] ^= 0x80; //联合体最高位取反
printf("%f\r\n",p->a);//-1.0

5、枚举

  • 枚举的典型用法是用来定义命令码。
  • 枚举可以是重复值。
  • 枚举中的标识其本质是常量也即是和 const int a = 1 效果是一样的。所以在程序里不能定义与之重名的其它变量。
  • 28
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值