位字段
:【专用于结构 体,结构体成员的类型必须是:int || unsigned int有时侯,结构体成员表示的数据很小,就用几个位来表示】。
例如
struct data1
{
unsigned a : 1; // 1就是一个bit,范围:0~1
int : 31; // 无名字段,不可访问,只是占位
unsigned b : 2; // 2就是er个bit,范围:0~3
unsigned c : 32; // 32个bit 范围:ffffffff~0
int : 0; // 0字段,不可访问,只是占位 整个字剩下的位,全部写0
}s1;
注意
1 位字段:下一个字段不够在剩余的bit存放时,必须另起一个字。字段不能跨字 所以 bit的范围最大就是32。
2 超出字段表示的数据范围,结果不可预期
3 字段不可取地址
对齐
结构体内成员对齐规则:
1、我们的结构体变量本身就是在4字节对齐的位置,编译器帮我们做的事。
2、第一个成员,就从结构体开始的地址处,存放。这个元素,具体占多少字节,由紧挨着下个元素决定。
3、整个成员变自身都对齐 了,还没有结束。
4、整个结构体还要是默认字节对齐的最小整数倍。
5,结构体默认的字节对齐:成员变量最大的那个类型所占字节
例如
【1】
typedef struct data
{
int a; // 4
char b; // 1 + 1(填充)
short c; // 2 最后整个结构体自身还要对齐
double d; //8
}D;
【2】 //16
struct da
{
int a;
short b;
int c;
double e;
}; // 16
struct data
{
char a; //1+1
short b; //2
int c; // 4
double e; // 8
struct da s; // 16
char ch;
}s ;
//1、不会因为有结构体成员,而影响你的基本类型,决定默认对齐
//2、里面的结构体对齐方式,已经在外面决定了(遍历整个完整结构体)
对齐指令
1 形式 #/*#pragma pack(n) (1、2、4、8、.....)
...........
#pragma pack()
【这两个配合使用,表示一个区间,只有这个区间内的结构体享受这个待遇。设置 n 就是n对齐。设置为1,就是不对齐】
2作用
1、充分利用内存空间,牺牲了速度,降低了访问效率。
2、提高效率、性能,牺牲了内存空间。
3 总结 :你指定的对齐方式和结构体自身默认对齐方式,俩者取最小的。
例如
#pragma pack(2)
struct data // 16
{
int a;
char b;
double c;
}s;
#pragma pack()
printf("sizeof(s) = %d.\n", sizeof(s)); // 1字节对齐 13
printf("sizeof(s) = %d.\n", sizeof(s)); // 2字节对齐 14
结构体成员变量的问题
struct data
{
int a; // 4 + 4(padding)
// int b;
struct data *p_next; //64bit机器指针永远永远占8字节
}S;
struct data1
{
int a;
// struct data1 s; // 1、【此时结构体类型(定义)不完整 2、C语言不准】
}s;
linux内核里的两个宏
1
#define off_set_of(type, member) ((long)&(((type *)0)->member))
意义:计算结构体内元素的偏移量
分析:1、(type *)0指向结构体零地址
2、((type *)0)->member得到了结构体某个成元变量名
3、给这成员变量名,取地址(相对于零地址),此时&(((type *)0)->member)表示是指针类型 \
4、强制类型转换成(long)。
例如
int off = off_set_of(struct data, c);
printf("off = %d.\n", off);
2
#define container_of(ptr, type, member) \
({typeof(((type )0)->member)_mptr = ptr; \
(type )((char)_mptr-off_set_of(type, member));})
意义:ptr是结构体里成员的指针,通过调用这个宏计算出结构体的首地址
分析: 1、得到结构体成员变量的类型
2、指针赋值(得到真实成员变量的地址值)
3、减去偏移量得到一个数字,该数字和结构体本身首地址在数值上一样
4、最后强制类型转换为结构体指针类型
例如
struct data *p = container_of(p_e, struct data, e);
struct data *p = container_of(&s.e, struct data, e);
printf("p = %p.\n", p);
网络结构体
头文件 #include
#include
#include
#include
#include
#include
#include
【这些头文件包括】
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t; // 16的无符号short类型
【第一个结构体】
struct in_addr
{
in_addr_t sin_addr;
};
/Structure describing an Internet socket address./
【第二个结构体】
struct sockaddr_in
{
__SOCKADDRCOMMON (sin); /AF_INET IPv4 Internet protocols 指定网络地址类型/
in_port_t sin_port; /Port number. 端口号 16位无符号short/
struct in_addr sin_addr; /Internet address. 具体的网址32位无符号int/
}
【首先宏定义】
#define PORT 0x1234
#define ADDR "19.16.1.22" //把本地ip地址转换成网络字节序地址
演示网络结构体赋值
【第一种 】【输出型函数】
void set_net_struct(struct sockaddr_in *p)【输出型函数】
{
p->sin_family = AF_INET;
p->sin_port = htons(PORT);
p->sin_addr.s_addr = inet_addr(ADDR);
}
int main(void)
{
struct sockaddr_in s; //赋值
set_net_struct(&s); //出入头文件中结构体的地址
} printf("网络字节序地址:0x%x.\n", s.sin_addr.s_addr);
【第二种】
int main(void)
{
1、struct sockaddr_in定义这种类型的结构体变量
struct sockaddr_in s;
2、填充你这个结构体各成员
s.sin_family = AFINET;
s.sin = AF_INET; (错误)
//但是已经错了,你的发送数据包,肯定找不到对方。
//传送过去的IP/PORT都是网络字节序,大端模式:低位(字节)要放到高地址。
s.sin_port = htons(PORT);
printf("s.sin_port = 0x%x.\n", s.sin_port);
s.sin_addr.s_addr = inet_addr(ADDR);
//0x16011013
printf("网络字节序地址:0x%x.\n", s.sin_addr.s_addr);
#endif
return 0;
}
柔性数组
:【1 是在结构体里,有一个数组,必须是结构体最后的一个元素,而且由特定形式[]或[0];
2 整体的结构体至少有两个成员变量】
不好之处:1、p(字符串)压根就没用,2、每次要用字符串时,很麻烦(char *)(p+1)。。
typedef struct data
{
int a;
char *p; // 结构体里存储的字符串, 和你结构体时分离的。
}D;
类型
typedef struct
{
int len;
int arr[];
}S;
枚举
:【里面成员必须大写】
定义常量符号,就是宏定义常数的集合体
比如:四季,星期,意义相关的常数
状态机:1、根据当前状态,这个周期结束,就成了下一个状态。
2、根据你的当前状态,还和你的输入有关。比如:fpga, GUI(用户图形交互界面)
例题
密码锁
typedef enum STATE
{
STATE1,
STATE2,
STATE3,
STATE4,
STATE5,
STATE6,
STATE7,
}S;
int main(void)
{
int num = 0;
//1、密码锁初始状态
S current_state = STATE1;
// 输入密码,进行解锁
printf("输入一个密码数字:");
while (1)
{
scanf("%d", &num);
printf("num = %d.\n", num);
//解锁子开始
switch (current_state)
{
case STATE1:
if (num == 9)
{
current_state = STATE2; // 用户每输对一次,进入下一状态
}
else
{
current_state = STATE1;
}
break;
case STATE2:
if (num == 5)
{
current_state = STATE3; // 用户每输对一次,进入下一状态
}
else
{
current_state = STATE1;
}
break;
case STATE3:
if (num == 2)
{
current_state = STATE4; // 用户每输对一次,进入下一状态
}
else
{
current_state = STATE1;
}
break;
case STATE4:
if (num == 7)
{
current_state = STATE5; // 用户每输对一次,进入下一状态
}
else
{
current_state = STATE1;
}
break;
default:
current_state = STATE1;
break;
}
if (current_state == STATE5)
{
printf("开了锁,请进!.\n");
//break
return 0;
}
if (current_state == STATE1)
{
printf("初始状态.\n");
}
}
/*
E e = EAST;
printf("e = %d.\n", e);
e = xq;
printf("e = %d.\n", e);
*/
return 0;
}
C++里面面向对象,多态
1 结构体各成员默认是public
2 class在C++里,是一个类,所有成员和方法(函数)是si(private)有的,只能在类里进行访问
3 struct里面不能包含函数,但是可以包含函数指针
4 不能有函数
struct data
{
int a;
int b;
p_func p;
//不能有函数
// int add(int a, int b);
}s;
//填充结构体
void set_func(struct data p)
{
p->a = 2;
p->b = 3;
// p->p = add;
p->p = mul_a_b;
}
//业务函数
int cnt_func(struct datap_str)
{
return p_str->p(p_str->a, p_str->b);
}
int main(void)
{
set_func(&s);
int ret = cnt_func(&s);
printf("ret = %d.\n", ret);
return 0;
}