一、结构体位域
1.1 结构体位域的基础
结构体位域:把结构体字节大小扣到极致的一个类型,以bit单位
格式:
struct 位域体名
{
数据类型 位域名:位域大小;
数据类型 位域名:位域大小;
...
};
解析:
位域体名:可有可无,满足命名规范
数据类型:只能是整数(char\short\int\long\long long)
位域名:可有可无,如果不屑,则默认该位不可以使用,结果位0
位域大小:不可以大于数据类型的总位值
eg:
struct A
{
int a:10;
int b:20;
char c:6;
...
};
struct B
{
int a:10;
int :20; //20位不可以使用,默认是0
char c:6;
...
};
1.2 结构体位域的大小
1.结构体位域的各个数据类型一致,且总和小于最宽数据类型的字节大小
分配字节单位是以最宽数据类型字节大小分配
struct A
{
int a:2;
int b:3;
int c:20;
};
结构体分配最宽成员的字节大小,int---->32
a:2 剩余30bit, b:3 剩余27 c:20 剩余7位
共4字节
2.结构体位域的各个数据类型一致,且总和大于最宽数据类型的字节大小
不可以跨字节存储
struct B
{
int a:20;
int b:23;
int c:7;
};
最宽数据类型int--->32bit
a:20 剩余12, b:23不够存储,则需要重新申请4字节存储b
b:23 剩余9 c:7 剩余2
共8字节
3.结构体位域的各个数据类型不一致,且总和大于最宽数据类型的字节大小
struct C
{
char a:7;
short b:15;
int c:30;
long d:20;
long long e:53;
};
最宽long long -->8字节---》64bit
a:7 剩余57 b:15:42 c:30 剩余12 d:20 重新8字节,
剩余44 e:53 重新申请8字节
共24字节
1.3 结构体位域的使用
struct C
{
char a:7; -2^6 2^6-1
short b:15; -2^14 2^14-1
int c:30; -2^29 2^29-1
unsigned long d:20;
long long e:53;
};
struct C num={128,20,30,40,50};
printf("%d",num.a);
char -128,127
char a=128 --->a=-128
char b=129 --->b:-127
char c=130 --->c:-126
如果数据在赋值时出现数据溢出,则会从最大值到最小值上
二、typedef类型重定义
typedfef: 类型重定义
格式: typedef 数据类型 别名;
1.typedef: 类型重定义
2.数据类型:基类型,构造类型,空类型,指针类型
3.别名:满足命名规范
size_t 在64操作系统unsigned long 32操作系统unsigned int
typedef int size_4; //把int起别名size_4 int--->sise_4
int a;--->size_4 a;
typedef unsigned long size_t;
typedef unsigned int size_t;
int age;
char name[100];
}p_t;
方式1:
struct Person a;
typedef struct Person p_t; //struct Person起别名p_t
p_t a;
int a;
int arr[2];
int *p;
typedef int a,arr[2],*p; a-->int arr-->int[2] p-->int*
typedef int a;
typedef float a;
C语言变量
int a;
int arr[3];
int arr[2][3];
int* p;
int** p;
int (*p)[3];
int *p[3]
int (*p)();
int *p()
C类型
int ;
int [3];
int [2][3];
int* ;
int** ;
int (*)[3];
int *[3]
int (*)();
int *()
typedef和类型的结合
typedef int a_t;//a_t类型别名,表示int
typedef int arr_t[3] ;//arr_t类型别名,表示int [3]
int arr[3]--->arr_t b;
typedef int arr_t[2][3];
typedef int* p_t;
typedef int** p_t;
typedef int (*p_t)[3];
typedef int *p_t[3]
typedef int (*fun_t)();
typedef int *fun_t();
eg:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
typedef int size_4;
size_4 a=100;
printf("a=%d\n",a);
typedef int arr_t[2][3];//arr_t等价int [2][3];
arr_t b={1,2,3,4,5,6};
for(int i=0;i<2;i++)
{
for(int j=0;j<3;j++)
printf("%d\t",b[i][j]);
putchar(10);
}
return 0;
}
三、宏
宏:宏只做替换,不做计算,不做正确性的检测
宏发生在预处理阶段
3.1 简单宏
格式:#define 宏名 宏体
1.#define 定义宏
2.宏名:满足命名规范,一般大写
3.宏体:默认时字符串
4.宏是没有类型,不需要在最后加;宏不是C语句
5.宏默认是全局,一般定义在主函数的上面
#define A 100
#deifne PAI 3.14
#define CH 'a'
#define STRING "hello"
#define size_4 int size_4的值int
3.2 宏函数
3.2.1 自定义宏函数
1.({})
格式:
#define 宏函数名(参数列表) ({C语句1;C语句2...C语句n})
解析:
1.参数列表:没有数据类型,多个参数之间使用逗号隔开
2.({}):当C语句只有一条时默认可以省略不写
3.({}): 默认存在返回,返回最后一个表达式
4.宏体建议只写一行,换行需要使用\链接父,进行连接
eg:使用宏计算两个数的最大值
#define MAX1(x,y) x>y?x:y
#define MAX(x,y) ({int max;\
if(x>y)\
max=x;\
else \
max=y;\
max;})
int main(int argc, const char *argv[])
{
int a=11,b=22;
int max=MAX(a,b);
a>b?a:b;
printf("max=%d\n",max);
printf("max1=%d\n",MAX1(a,b));
return 0;
}
2. do{}while(0)
格式:
#define 宏函数名(参数列表) do{C语句1;C语句2....}while(0)
1.参数列表:没有数据类型,多个参数之间使用逗号隔开
2.do{}while(0) 后面不加分号
3.默认不存在返回
4.宏体建议只写一行,换行需要使用\连接符,进行连接
eg:使用宏计算两个数的最大值
#define MAX(x,y) do{if(x>y)printf("%d",x);else printf("%d",y);}while(0)
3.2.2 系统自带的宏函数
1.判断宏是否为真,多用注释
#if 宏名
C语句1;
#endif
执行过程:如果宏为真,则执行C语句,否则跳过
2.宏的双分支
#if 宏名
C语句1;
#else
C语句2;
#endif
执行流程:如果宏为真则执行C语句1,否则执行C语句2
3.宏的多分支
#if 宏名1
C语句1;
#elif 宏名2
C语句2;
....
#else
C语句n;
#endif
执行流程:如果宏1为真则执行C语句1,结束
如果宏1为假,则判断宏2,如果宏2为真,则执行C语句2,结束
以此类推
4.判断宏已经定义
#ifdef 宏
C语句1;
C语句2;
#endif
执行流程:判断宏已经定义了,如果定义了,则执行C语句
5.判断宏没有定义
#ifndef 宏
C语句1;
#endif
执行流程:判断宏没有定义了,如果没有定义,则执行C语句
6.判断多个宏已经定义
#if defined(宏1) &&defined(宏2)
C语句1;
#endif
执行流程:判断宏已经定义了,如果定义了,则执行C语句
7.判断多个宏没有定义
#if !defined(宏) #if undefined==宏
C语句1;
#endif
执行流程:判断宏没有定义了,如果没有定义,则执行C语句
8.# 把宏体转换为字符串
#define FUN(a) #a
9,## 连接
#define FUN(a,b) a##b
#讲解案例:
##的讲解案例:
3.2.3 多文件编译
1.头文件:预处理命令,全局变量,头文件,函数生命
2.main函数
3.自定义函数
#include <>: 默认在系统库中查找头文件 库路径是/usr/include/
#include "":现在当前目录下查找头文件,找不到则在系统库中查找
3.3 宏和typedef之间的区别
1.宏发生在预处理阶段,typedef 编译阶段
2.宏不是C语句,typedef是C语句
3.宏属于替换,typedef类型重定义
4.宏只能做基类型的简单替换,typedef可以重定义任何类型
typedef int size_4 typedef int arr[2]
#define size_4 int
四、共用体union
4.1 认识共用体的格式
union 共用体名
{
数据类型 变量名;
数据类型 变量名;
····
}; //定义了一个结构体类型
4.2 共用体的地址
各个成员变量共享同一片空间
共用体字节大小等于最宽成员的字节大小,满足字节对齐原则
4.3 共用体的使用
1.当没有指定成员时,默认给第一个成员赋值
typedef union A
{
int a;
char b;
float c;
}A_t;
A_t num={100}; 默认给第一个成员a赋值
2.指定成员赋值
A_t num2={.c=3.14};
3.定义变量后赋值
A_t num3;
num3.a=100;
num3.b='a';
num3.c=3.14
此时最终结果是最后一次赋值的结构
4.4 使用union解决大小端存储
typedef union A
{
int a;
char b;
}A_t;
A_t num={0x12345678}
if(num.b==0x78)
printf("little\n");
else
printf("big\n");
五、枚举
5.1 枚举的概念
枚举:防止魔鬼数字
枚举:列举所有集的有序序列的常量集合
枚举属于基类型
#define A 1
#define B 2
#define C 3
enum 枚举名
{
变量名1,变量名2,....
};
1.enum:枚举关键字
2.枚举名:满足命名规范
3.变量名:没有数据类型,类型是自定义的枚举类型 enum 枚举名
5.2 枚举的使用
1.默认没有赋值
enum NUM
{
A,B,C A默认结果是0,后面的一次递增
};
printf("A=%d\n",A);
2.指定赋值
enum NUM
{
A,B=50,C A默认结果是0,B=50 C=51
};
enum NUM
{
A=20,B=50,C=31 A默认结果是20,B=50 C=31
};
3.枚举变量
enum NUM
{
A=20,B=50,C=31 A默认结果是20,B=50 C=31
};
enum NUM a=100; //a是枚举变量,可以修改,ABC成员不可以修改
六、虚拟内存划分