构造类型
-
结构体:
- 类型描述:结构体的类型描述不占空间,因此不能在结构体定义过程中进行初始化。
struct 结构体名{ 数据类型 成员1; ....; };//别丢掉分号
- 嵌套定义的实现:
struct father_infro{ int age; char* name }; struct std_infro{ int age; char* name; struct father_infro father; };//别丢掉分号 //也可以如下定义 struct std_infro{ int age; char* name; struct father_infro{ int age; char* name }father; };
- 定义变量(变量,数组,指针),初始化及成员引用
- 三种引用:
- 变量名.成员名
- 指针->成员名
- (*指针).成员名
//=========================变量定义==============================// struct std_infro a = {12,"you",{33,"you father"}}; //a其实也是结构体的起始地址 //如果想仅初始化一部分则如下: //struct std_infro a = {.name = "you", .father_infro.age = 35}; printf("a.age = %d", a.i); //普通成员引用:变量名.成员名 printf("a.father.name = %s", a.father.name); //嵌套成员引用:变量名.变量名.成员名 //=========================变量定义==============================// //=========================指针定义==============================// struct std_infro a = {12,"you",{33,"you father"}}; struct* std = &a; printf("a.age = %d", a->i); printf("a.father.name = %s", a->father.name); //为何father不用指针?--- 观察到,father并非指针类型 //=========================指针定义==============================// //=========================数组定义==============================// struct std_infro a[2] = {{12,"you",{33,"you father"}}, {15,"me",{35,"my father"}}}; struct std_infro* p = a; //=========================数组定义==============================//
- 三种引用:
- 结构体所占内存大小:
-
结构体开辟占用空间时存在内存对齐问题
-
为什么要字节对齐?
- 主要是为了提高内存的访问效率,比如intel 32位cpu,每个总线周期都是从偶地址开始读取32位的内存数据,如果数据存放地址不是从偶数开始,则可能出现需要两个总线周期才能读取到想要的数据,因此需要在内存中存放数据时进行对齐。
-
如何计算对齐后的所占大小?公式中0可以除任何值
- 公式:当前存放地址addr能整除sizof(当前变量)则存入,不然地址进行+1偏移
- 公式:当前存放地址addr能整除sizof(当前变量)则存入,不然地址进行+1偏移
-
因此我们需要注意结构体在传给对方数据时候不能做对齐,如下做
struct 结构体名{ 数据类型 成员1; ....; }__attribute__((packet));//这样就是各个成员的累加和
-
- 结构体的传参问题:
- 传参方式:直接传参,间接传参
- 为什么更希望用指针传参?
- 函数接受参数后,形参也需要开辟和实参一样的大小空间进行零时存储,那么显然,如果直接出入结构体,则形参的临时大小完全取决于实参的大小,这样做无疑会浪费存储空间,而指针传参的最大优点在于,其开辟临时内存大小只能是当前环境的一个指针的开销。
- 有名结构体和无名结构体的区别?
- 无名结构体一定要把所有变量定义出来,不然以后找不到名字,无法进行后续的定义。
- 类型描述:结构体的类型描述不占空间,因此不能在结构体定义过程中进行初始化。
-
共用体
-
产生及意义:多个成员共用一部分内存—根据成员中最大的一个进行内存分配—在某一时间段只能有一个成员占用
- 理解:对于一个注册系统,可以允许多个用户同时注册(结构体),但每个用户的注册性别只能是一种性别(共用体)
-
类型描述:
union 共用体名 { 数据类型 成员名1; 数据类型 成员名2; ... };
-
嵌套定义:
union union_struct{ int num1; float num2; char num3; struct{ int arr[10]; float f; }infro; };//占内存大小为44个字节 union struct_union{ int num1; float num2; char num3; union { int a; char f; }infro; };//不考虑地址对齐占内存大小为10个字节
-
嵌套解决的问题:
- 什么是大端格式什么是小端格式?
- 大端:数据低位保存在高地址中 C51
- 小端:数据的低位保存在低地址中 ARM
- 需求:试问一个数的高16位和低16位的和是什么?//使用共用体,不要用位运算
union { struct { uint16_t i; uint16_t j; }x; uint32_t y; }a; int main() { a.y = 0x11223344; printf("%x\n", (a.x.i + a.x.j)); //输出4466 //解释--因为union中struct和uint32均占用了4个字节,虽然给y进行了赋值, //但是由于两者其实是同时处在一个存储地址中,因此可以直接读取,这是存储的特点决定的 return 0; }
- 什么是大端格式什么是小端格式?
-
定义变量(变量,数组,指针),初始化及成员引用:
union test_union{ int num1; float num2; char num3; }; int main() { union test_union test; union test_union* p; union test_union arr[3]; test.num1 = 3; p = &test; p->num3 = 'h'; printf("sizof(test) = %d\n", sizeof(test)); //共用体的内存分配是按照最大类型分配的---output:4 printf("num1 = %d\n", test.num1); //output:3 printf("num2 = %f\n", test.num2); //output:0.000000---无意义 printf("num3 = %c\n", p->num3); //output:0.000000---无意义 return 0; }
-
占用内存大小
-
函数传参(值,地址)
-
位域:
- 共用体的一种形式—存储上以位bit来占位
union test_union{ struct { char i:1;//存储几个bit可以指定 char j:2; }a; char b; };
-
枚举—一种特定情况的集合
-
定义:
- enum 标识符{
成员1;
成员2;
…
};
enum Gender{ MALE, FEMALE };//MALE默认0,FEMALE默认1 //可以给任意一个成员直接赋值,后面的值依次加1,成员的赋值也是可以重复的 int main() { enum Gender xiaoming = MALE; //xiaoming = FEMALE;---error写法 }
- enum 标识符{
-
常见使用:将enum当作一连串的宏来使用—将其当作有值的宏处理
- 但是这不意味着他可以替代宏—宏可以传参等功能
enum State{
STATE_RUNNING = 1,
STATE_CANCELED,
STATE_OVER = 0
};//表示当前状态
struct StateMachine{
int state;
int num;
};
int main()
{
struct StateMachine Project1;
int i;
for(i = 0; i < 3; i++){
Project1 = i;
switch(Project1.state)
{
case 0:
Project1.state = STATE_RUNNING;
printf("Now_State = %d\n", Project1.state);
break;
case 1:
Project1.state = STATE_CANCELED;
printf("Now_State = %d\n", Project1.state);
break;
case 2:
Project1.state = STATE_OVER;
printf("Now_State = %d\n", Project1.state);
break;
default:
Project1.state = STATE_OVER;
printf("Now_State = %d\n", Project1.state);
break;
}
}
}
动态内存分配
- auto类型—栈上分配 动态内存—堆上分配
- 调用库函数:#include<stdlib.h>
- malloc:一次申请的总字节数—失败返回NULL
- void* malloc(size_t size);//堆上分配后给void* size计算是字节大小
- calloc:连续申请多少个字节—失败返回NULL
- void* calloc(size_t nmemb, size_t size);//想分配nmemb个成员,每个成员大小size
- realloc:重新分配动态内存空间
- void* realloc(void *ptr, size_t size);
- 解释:ptr必须是malloc和calloc调用返回的指针,重新分配size大小的空间
- free:
- void free(void *ptr);
- 原则:谁申请谁释放—防止内存泄漏
- 面试问题
void func(int* p, int length){
p = malloc(sizof(int)*length);
if(p == NULL){
exit(1);
}
return ;
}
int main()
{
int length = 100;
int *p = NULL;
func(p, num);
free(p);
return 0;
}
/*
该代码出现什么问题了么?
已经产生了内存泄漏原因如下
1.main中的p和func中的p本质上是 两个 指针变量,只不过func中的p赋值了main中p的数值
2.在func中,指针p由于malloc而被指向了新的区域,并开辟了空间
3.main中的free无意义,因为main中的p一直都指向NULL并没有改变过
代码改进如下
*/
/*********************方法一********************/
void func(int* *p, int length){
//int** p 表示 一个指针p指向int*类型的指针
*p = malloc(sizof(int)*length);
if(p == NULL){
exit(1);
}
return ;
}
int main()
{
int length = 100;
int *p = NULL;
func(&p, num);//p也是一个变量取它的地址就可以对它进行修改
free(p);
return 0;
}
/*********************方法一********************/
int* func(int* p, int length){
*p = malloc(sizof(int)*length);
if(p == NULL){
exit(1);
}
return p;
}
int main()
{
int length = 100;
int *p = NULL;
p = func(p, num);//更新p
free(p);
return 0;
}
一个关键字的讲解 typedef
- 作用:为已有的数据类型改名—对于不同位数的机器端可以规避同一类型位数的不同
- 格式:typedef 数据类型 新名称; typedef int INT
- 与define的区别:
.#define IP int* 定义—IP p,q; —> int *p,q;
.typedef int *IP; 定义—IP p,q; —> int *p, *q; - 对于数组的定义:typedef int ARR[6] —> 理解为int [6] -> ARR — ARR a; --> int a[6];
- 对于结构体的定义:
typedef struct{
int i;
float f;
}NODE,*NODEP;//struct被NODE代替 struct* 被NODEP代替
NODE p1 --- 代表p1是一个结构体
NODEP p2 --- 代表p2是一个指向结构体的指针
- 对于函数的定义:
typedef int FUNC(int); FUNCPP p// ---> int(int)的函数被代替为FUNC
typedef int *FUNCP(int); FUNCPP p// ---> int* (int)的函数被替代为FUNCP 给指针函数改名
typedef int *(*FUNCPP)(int); FUNCPP p// ---> int* (*p)(int)FUNCPP是一个指向指针函数的指针 给函数指针改名