嵌入式学习Day05(C宏定义、条件编译、用户自定义结构体、位域)

嵌入式学习Day05

宏定义

用法

定义 : #define 宏名 常量
作用 :在程序中可以使用宏名替代常量
规范:宏名一般用大写

#include<stdio.h>
/*宏定义*/
#define   NAME "LiMing"
int main(){
	//使用宏定义
	printf("NAME 被定义为 %s \n",NAME);

}

root@wangjudealy:/learn# gcc 0317.c 
root@wangjudealy:/learn# ./a.out 
NAME 被定义为 LiMing 

宏函数

定义:#define 宏函数名 函数体
注意:宏的作用是原样替换

#include<stdio.h>
/*宏定义*/
#define   NAME "LiMing"
/*宏函数*/
#define FUN(x)  (x*x)
int main(){
	//使用宏函数
	printf("result = %d \n",FUN(10+10));//因为宏是原样替换,所以此处实际代码为: 10+10*10+10 结果为120
	printf("result = %d \n",FUN(20));

}


root@wangjudealy:/learn# ./a.out 
result = 120 
result = 400 

do while(0) 在嵌入式中的应用

作用:

  1. 代码只执行一次,防止使用宏时报错
  2. 可以扩展宏定义

不使用do while(0)

#include<stdio.h>
void f1(){
	printf("this is fu1\n");
}
void f2(){
	printf("this is fu2\n");
}
void f3(){
	printf("this is fu3\n");
}
#define TEST() f1();f2();

int main(){

	int p =0;
	printf("请输入p:\n");
	scanf("%d",&p);
	if(p>1)
		TEST(); //因为宏是原样替换,所以此处相当于执行了两句 f1();f2(); 
			//此时程序编译就会报错,因为如果不加{},if 后只能跟随一句
	else
		f3();
	return 0;
}

root@wangjudealy:/learn# vi  0317.c 
root@wangjudealy:/learn# gcc 0317.c 
0317.c: In function ‘main’:
0317.c:21:2: error: ‘else’ without a previous ‘if’
   21 |  else
      |  ^~~~

使用do while(0)

#include<stdio.h>
void f1(){
	printf("this is fu1\n");
}
void f2(){
	printf("this is fu2\n");
}
void f3(){
	printf("this is fu3\n");
}
#define TEST() do{\
	f1();\
	f2();\
	if(1){\
		printf("其他扩展部分\n");\
	}\
	}while(0)
//注意:因为是原样替换,所以while(0)后没有; 
int main(){

	int p =0;
	printf("请输入p:\n");
	scanf("%d",&p);
	if(p>1)
		TEST(); //因为宏是原样替换,所以此处相当于执行了两句 f1();f2(); 
			//此时程序编译就会报错,因为如果不加{},if 后只能跟随一句
	else
		f3();
	return 0;
}

root@wangjudealy:/learn# gcc 0317.c 
root@wangjudealy:/learn# ./a.out 
请输入p:
12
this is fu1
this is fu2
其他扩展部分

条件编译

格式:#if 宏名
语句1
#else
语句2
#endif
作用:判断宏名是否为真(0为假,其他值为真),已定义执行语句1,否则执行语句2。

格式:#ifndef 宏名
语句
#endif
作用:判断宏名是否存在,不存在则执行语句,否则不执行
#ifndef===> if no define,同理,也有 #ifdef

C语言多文件编程

extern关键字

格式

功能:使全局变量、函数可以扩展到其他文件中使用
格式1(修饰全局变量):extern 数据类型 全局变量名
格式2(修饰函数):extern 返回值类型 函数名(数据类型 参数1,数据类型 参数2,…)

使用

在fun.c中定义全局变量和函数,在main.c中调用fun.c中定义的全局变量和函数

fun.c

#include <stdio.h>

void fun1(){
	printf("fun.c 的 fun1函数\n");
}
int global = 2024;

main.c

#include<stdio.h>

//use extern
extern int global;
extern void fun1();


int main(){
	fun1();
	printf("global = %d \n",global);
}

编译执行
注意:extern 所声明的函数所在的文件要一起编译,否则会报错

root@wangjudealy:/learn/day05# gcc main.c fun.c
root@wangjudealy:/learn/day05# ./a.out 
fun.c 的 fun1函数
global = 2024 

多文件编程框架

头文件(.h 文件)
  1. 防止其他头文件重复定义宏(条件编译)
  2. 包含函数声明
  3. 宏定义
  4. 需要使用的头文件
  5. 结构体或枚举类型的声明
  6. 全局变量的声明
    ,h文件格式
#ifndef __HEAD_H__
#define __HEAD_H__
语句....

# endif
功能文件(.c 文件)

存放用户所需要的逻辑代码函数,即API,不包含主函数main()

C语言高级

结构体

声明
struct 结构体名{
	类型 变量名1;
	类型 变量名2;
	......
	......
	......
}
结构体变量的定义

1、先声明,再定义

struct 结构体名 变量名;      

2、声明时定义

struct 结构体名{
	类型 变量名1;
	类型 变量名2;
	......
	......
	......
}变量名列表;

3、不适用类型名,直接定义

struct {
	类型 变量名1;
	类型 变量名2;
	......
	......
	......
}变量名列表;

typedef的使用

typedef :给已经存在的类型取一个别名

typedef struct 结构体名{
	类型 变量名1;
	类型 变量名2;
	......
	......
	......
}A;    //将 struct 结构体名 定义了一个新的类型名   ‘struct 结构体名 a’ == ‘A a’
访问结构体内部成员变量
  1. 普通成员变量通过 '.'来访问
  2. 指针成员变量通过‘->’来访问(注意:结构体中成员为数组时,若要访问,数组名为地址
  3. 一般在 .h 文件中,或定义为全局变量
结构体大小计算

结构体变量大小比实际的大,因为存在字节对齐现象

联合体

联合体所有成员共享同一块内存。共用最大的一块内存
比如有两个成员,一个是char,一个是int,则这两个成员共用int类型成员的空间

声明
union name {
	类型  成员1;
	类型  成员2;
	...
	...
};
定义和访问成员变量

参考结构体

作用

1、可以使用联合体判断计算机是大端存储还是小端存储
2、可以节省内存空间,比如用户登录,先共用体可以先存用户名,再存密码

枚举类型

声明
enum name{
	常量名1;
	常量名2;
	常量名3;
	....
	....
	....
}
  1. 枚举元素都是常量
  2. 枚举元素没有赋值,第一个元素默认为0,以后元素递增

位域

简介

有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用0和1表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种数据结构,叫做“位域”或“位段”。位域将一个字节的二进位划分为几个不同的区域。并说明每个区域的位数,每个域有一个域名,可以按域名操作域。

声明
struct 位域结构名{
	类型说明字符1 位域名1:位域长度1; //最低位
	类型说明字符2 位域名2:位域长度2; //次低位
	.....
	类型说明字符n 位域名n:位域长度n; 
}

位域位数小于对应类型的位数
一个字节不够时,位域可以存放在下一个字节中,比容剩下两位,要定义6位的位域,可以定义一个2位的空域(无名位于),使下一个位域存放在下一个字节中
相邻位域字段类型相同,且位长之和小于类型总长,则它们相邻存储
相邻位域字段类型不同,根据编译器的差异有些会压缩
位域定义时可以穿插非位域成员
位域成员往往不占用一个完整的字节,有时处于不同的字节开头位置,因此使用&获取位域成员地址是无意义的

struct s2{
	
	unsigned int n1:1;

	unsigned int n2:1;
}s_2;
int main(){
	struct s2 * p=&s_2; 
	p->n1=0;  //这是允许的
	&s_2.n1  ; //这种操作无意义,且不允许
}
定义

参考结构体

代码示例
#include<stdio.h>
struct s1{
	unsigned int n1;

	unsigned int n2;
}s_1;
struct s2{
	
	unsigned int n1:1;

	unsigned int n2:1;
}s_2;
int main(){
	

	printf("结构体==》sizeof(s1)=%d\n",sizeof(s_1));

	printf("位 域==》sizeof(s2)=%d\n",sizeof(s_2));

	return 0;
	
}

root@wangjudealy:/learn/day05# ./a.out 
结构体==》sizeof(s1)=8
位 域==》sizeof(s2)=4

### C语言结构体的使用方法和特性 #### 的基本概念 在C语言中,(Bit Fields)是一种特殊的数据结构,它允许程序员在一个字节内定义多个二进制字段。这使得可以在有限的空间内存储多个布尔值或枚举值[^1]。 #### 定义的方式 通过在结构体内声明成员变量并指定其宽度来实现。基本语法如下: ```c struct { unsigned int member1 : n1; unsigned int member2 : n2; } yourStructName; ``` 其中 `unsigned int` 表示成员的数据类型,可以是 `signed int`, `unsigned int` 或 `_Bool`;而冒号后的数字指定了该成员占据多少个比特[^4]。 #### 实际应用案例 下面是一个具体的例子展示了如何创建包含不同长度结构体: ```c typedef struct _net_pro_cdu { unsigned char acs : 4; // 占用前四 unsigned char : 0; // 不占任何, 起分隔作用 unsigned char ace : 4; // 接着占用后面的四个置 unsigned char dve : 4; // 新的一组四比特序列 } Ncdu; ``` 这里定义了一个名为Ncdu的新类型,内部有三个分别占有4bit空间的成员acs、ace以及dve,并且还有一个匿名零宽用来强制结束当前字节边界上的填充[^3]。 #### 平台依赖性与移植注意事项 值得注意的是,尽管能够有效地利用内存资源,但由于编译器之间存在差异,对于相同代码片段所产生的实际布局可能有所不同。这意味着当涉及到跨平台开发时,应当特别注意确保程序逻辑不会受到这种不确定性的负面影响[^2]。 #### 组合运用——联合体配合 除了单独作为结构体的一部分外,还可以将与其他高级特性结合起来使用。比如把放在联合体内可以让同一块物理地址既能按整体读写又能逐操作,从而进一步增强了灵活性[^5]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值