嵌入式养成计划-15-内存空间分配及动态申请与释放,typedef,结构体

四十一、内存空间分配及动态申请与释放

41.1 虚拟内存空间分配

在这里插入图片描述

  • 栈区:满足栈的思想,先进后出,先定义变量后分配空间
  • 堆区:满足队列的思想,先进先出,先定义变量先分配空间
    示例:
    在这里插入图片描述

代码赏析:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int a;				// 全局,静态区,未初始化.bss
int b = 100;		// 全局,静态区,初始化,.data
static int c;		// 静态变量 未初始化,.bss
static int d = 200; // 静态变量 ,初始化 .data
const int e = 10;	// const修饰的全局,在静态区,.ro

char str[6] = "hello"; // str全局,初始化..data
					   //"hello":字符串常量,空间在ro
					   // 把字符串常量赋值一份给str,可以修改
char *p = "hello";	   // p全局,初始化.data
					   //"hello":字符串常量,空间在ro
					   // p直接指向ro段的首地址,不可以修改

int *p = (int *)malloc(4); // 报错,全局不可以调用函数

int main(int argc, const char *argv[])
{
	int a;				  // 局部,栈区
	int b = 100;		  // 局部,栈区
	static int c;		  // 静态局部变量 静态区,未初始化.bss
	static int d = 200;	  // 静态局部变量 静态区,初始化.data
	const int e = 10;	  // const修饰的局部在栈区
	char str[] = "hello"; // str:在栈区,hello在静态只读段

	char *p = "hello"; // p在栈区,hello在静态区的只读段

	int *p = (int *)malloc(4); // p在栈区,malloc4字节在堆区

	return 0;
}

41.2 内存空间的动态申请和释放

原因: 堆区空间生命周期长(由程序手动申请,手动释放),程序长期运行时堆区节约空间

  • 在释放指针时,需要保存堆区空间的首地址,否则会造成空间泄露问题

41.2.1 malloc动态申请

malloc :

头文件:#include <stdlib.h>
格式	:
	void *malloc(size_t size);
参数	:	
	size_t size 堆区分配空间字节大小
返回值:	
	void *	
	通用类型指针,可以指向任意类型的地址,但是需要类型强转
	当堆区空间申请成功默认返回堆区首地址,失败返回NULL

使用格式:

分配单个空间:
	int a;
	int *p=&a
	int *p=(int *)malloc(sizeof(int));

分配连续空间:
	int arr[10]
	int *p=arr;
	int *p2=(int *)malloc(sizeof(int)*n);

41.2.1 free释放

free :

头文件:	
		#include <stdlib.h>		
格式	:
		void free(void *ptr);		
返回值:	
		无
参数	:	
		void *ptr   表示释放的指针

使用格式:
	int *p=(int *)malloc(sizeof(int));//一个整型所占用的空间
	free(p);// 释放表示这片空间自由不被占用,操作系统会重新给其他变量分配
	p=NULL; //防止野指针

野指针 :

1. 未初始化的指针,世界使用,成为野指针
	int *p;  //p计算机随机指向一片空间
	*p=100;
2. 指针指向数组,通过指针越界访问
	int arr[5];
	int *p=arr;
	*(p+5)=100;
3. 函数返回一个局部变量的地址
	int *fun()
	{
		int arr[4];//局部变量,调用函数arr申请空间,函数结束释放
		return arr;  
	}
	int main()
	{
		int *p=fun();   //野指针 
	}
4. 指针指向堆区空间,释放堆区空间
	int *p=(int *)malloc(sizeof(int));//一个整型所占用的空间
	free(p);// 释放表示这篇空间自由不被占用,操作系统会重新给其他变量分配
	*p=100;

四十二、typedef

42.1 typedef 类型重定义

typedef :

使用格式:  
	typedef   数据类型   类型别名;

1. 类型别名:满足命名规范
2. 类型别名可以是多个,多个之间使用逗号隔开
	typedef  int  size_4;		//	size_4等价int
	size_4 a=100;				//	使用size_4 定义变量并初始化

变量的定义

int a;        
int arr[10];
int arr[2][3];
int *p;
int **p;
int *p[3];
int (*p)[3];
int *p();
int (*p)();
int (*p[3])();

变量的数据类型

int ;        
int [10];
int [2][3];
int *;
int **;
int *[3];
int (*)[3];
int *();
int (*)();
int (*[3])();

数据类型和typedef结合

typedef int size_4;        
typedef int arr_t[10];		//arr_t不是数组变量名,是一个数组的类型
                        	//arr_t	等价于 int [10]
typedef int arr_t[2][3];
typedef int *p_t;
typedef int **p_t;
typedef int *p_arr_t[3];
typedef int (*p_t)[3];
typedef int *p();
typedef int (*p)();
typedef int (*p[3])();

42.2 宏 与 typedef 的区别

#define N 100    N=100
#define size_4 int    size_4=int

1. 宏属于宏替换,类型重定义起别名
2. 宏不属于c语句,类型重定义是C语句
3. 宏只能做基类型的替换,不可以做复杂类型
4. 类型重定义可以重定义任何复杂数据类型

typedef int arr_t[10]
#define N int [10]   不识别

四十三、结构体

43.1 结构体定义

  • 数组存储多个类型相同的数据
  • 如果需要存储类型不同的数据需要使用结构体

结构体: 存储类型相同或不同数据类型数据的构造类型
构造类型: 可以分割

定义格式:
    struct  结构体名
    {
        数据类型  成员变量1;   
        数据类型  成员变量2;
        数据类型  成员变量3;
        ...
        数据类型  成员变量n; 
    };

1. struct:
			定义结构体的关键字
2. 结构体名:
			满足命名规范,可以省略,无名结构体
3. {}	不可以省略
4. 分号;	不可以省略
5. 数据类型:
			基本类型,构造类型、空类型、指针类型
6. 成员变量的个数任意
7. 结构体描述不分配空间,直到定义结构体变量时

eg:	定义一个结构体,存储学生信息:姓名、性别、年龄,身份证,分数
    struct studnet
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
          char id[20];//身份证号
          float score;//分数            
    };
    
    结构体类型:struct studnet		如同		int	但内容不同
    struct student a;
    int a;

43.2 结构体变量以及初始化

间接初始化(常用)

间接定义变量: 定义结构体描述后,定义结构体变量

struct student
{
	char name[10];//姓名
	char sex;//性别
	int age;//年龄
};//直接
1. 按顺序表初始化
	struct student stu={"张三",'M',18};
	struct student stu={'M',18,"张三"};//报错
2. 不按顺序表初始化
    struct student stu={.sex='M',.age=18,.name="张三"};
3. 单个初始化
    struct studnet stu;
    strcpy(stu.name,"张三");
    stu.age=18;
    stu.sex='M'
4. 输入赋值
    struct studnet stu;
    scanf("%s",stu.name)//name是数组名,数组表示数组的首地址
    scanf("%d",&stu.age);
    scnaf("%c",&stu.sex);

直接初始化

直接定义变量: 定义结构体描述的同时定义结构体变量

1. 按顺序表初始化
    struct student
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu={"张三",'M',18},stu1;
2. 不按顺序表初始化
    struct student
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu={.sex='M',.age=18,.name="张三"};
3. 单个初始化
    struct student
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu;
    strcpy(stu.name,"张三");
    stu.age=18;
    stu.sex='M';
4. 输入赋值
    struct student
    {
          char name[10];//姓名
          char sex;		//性别
          int age;		//年龄
    }stu;
    scanf("%s",stu.name)//name是数组名,数组表示数组的首地址
    scanf("%d",&stu.age);
    scnaf("%c",&stu.sex);
5. 只有直接初始化才可以省略结构体名,无名结构体
    struct
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu={"张三",'M',18},stu1;

43.3 结构体数组

结构体数组: 实际上就是一维数组
定义格式: struct 结构体名 结构体变量名[常量表达式];

间接定义变量并初始化(常用)

struct student
{
	char name[10];//姓名
	char sex;//性别
	int age;//年龄
};//间接
1. 按顺序表初始化
    struct student stu[2]={{"张三",'M',18},{"李四",'W',20}};
    struct student stu[2]={"张三",'M',18,"李四",'W',20};
2. 不按顺序初始化
    struct student stu[2]={[1]={.sex='W',.name="李四",.age=20},
                            [0]={"张三",.sex='M',.age=18}};
3. 单个赋值初始化
    struct student stu[2];
    strcpy(stu[0].name,"张三");
    stu[0].sex='M';
    stu[0].age=18;
    strcpy(stu[1].name,"张三");
    stu[1].sex='M';
    stu[1].age=18;
4. 循环输入赋值
    struct student stu[2];
    for(int i=0;i<2;i++)
    {
         scanf("%s",stu[i].name);  //stu[i].name等价于name,就是数组名 
         scanf(" %c",&stu[i].sex);//stu[i].sex等价于sex,单字符输入需要加&
         scanf("%d",&stu[i].age);
    }

直接定义变量并初始化

1. 按顺序表初始化
     struct student
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu1[2]={{"张三",'M',18},{"李四",'W',20}},
    stu2[2]={"张三",'M',18,"李四",'W',20};
2. 不按顺序初始化
    struct student
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu[2]={[1]={.sex='W',.name="李四",.age=20},
                            [0]={.name="张三",.sex='M',.age=18}};
3. 单个赋值初始化
    struct student
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu[2];
    strcpy(stu[0].name,"张三");
    stu[0].sex='M';
    stu[0].age=18;
    strcpy(stu[1].name,"张三");
    stu[1].sex='M';
    stu[1].age=18;
4. 循环输入赋值
    struct student
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu[2];
    for(int i=0;i<2;i++)
    {
    	//stu[i].name等价于name,就是数组名 
        scanf("%s",stu[i].name);
        //stu[i].sex等价于sex,单字符输入需要加&
        scanf(" %c",&stu[i].sex);
        scanf("%d",&stu[i].age);
    }
5. 当直接初始化时,可以省略结构体名,无名结构体
     struct 
    {
          char name[10];//姓名
          char sex;//性别
          int age;//年龄
    }stu1[2]={{"张三",'M',18},{"李四",'W',20}},
    stu2[2]={"张三",'M',18,"李四",'W',20};

43.4 结构体指针(重点)

定义:

struct 结构体名 *指针名;

和基本数据类型的指针的 使用、初始化以及赋值的方式是一样的

访问结构体成员

  • 结构体不是基本数据类型,是构造类型
  • 一个结构体变量包含多个成员

访问方式:

  • 结构体变量.成员名;
  • 结构体指针->成员名;

示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu
{
    char name[100];
    int score;
};
int main(int argc, const char *argv[])
{
    struct Stu s1;
    //<==>int s1;
    printf("%p\n",&s1);
    struct Stu *p = &s1;   //定义了一个结构体指针p指向s1这个结构体变量
    printf("%p\n",p);  //结构体指针,指向结构体的首地址
 	s1.score = 100;
 	p->score = 90;      //直接通过结构体指针,访问到结构中的成员
 //s1  (*p).score
 	strcpy(p->name,"zhangsan");
    printf("姓名:%s\t成绩%d\n",p->name,s1.score);
    printf("%ld\n",sizeof(p));
    return 0;
}

43.5 结构体和typedef结合

typedef struct Stu
{
    char name[100];
    int score;
}Stu,*pstu;   
//把struct Stu重定义为Stu类型,后面可以直接使用Stu定义结构变量
//把struct *重定义为pstu类型,后面可以直接使用pstu定义结构体指针变量

上面的代码可以拆分表示为:

struct Stu
{
    charr name[100];
    int score;
}s1;

struct Stu
{
    charr name[100];
    int score;
}*pstu;

例如:

int a;  		//a是一个变量名
typedef int a;  //a被重定义过的int类型

使用示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Stu
{
    char name[100];
    int score;
}Stu,*pstu;   //把struct Stu类型重定义为Stu,
//把stu这个结构体指针类型,重定义为pstu

int a,*p;   //int a; int *p;

int main(int argc, const char *argv[])
{
    Stu s1;
    pstu p;   //定义了一个结构体指针变量p
    p = &s1;
    printf("%p\n",&s1);
    printf("%p\n",p);
    return 0;
}

43.6 结构体的大小

字节对齐的规则:

  1. 除了结构体中的第一个成员外,其他成员必须存储在其本身对齐量的整数倍位置
  2. 结构体的整体大小,需要是结构体中最大对齐成员对齐量的整数倍
  3. 结构体成员的对齐量 = (成员本身数据类型的大小<操作系统对齐数) ? 成员本身的大小 : 操作系统的对齐数 (在没有指定的情况下,32位按4Byte对齐,64位按8Byte对齐)

示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct A
{
    double d2;   //8
    char c1; //1
          //7             
    int *p;  //8
    int num1;
    short int s2;  
}a;  //64位  32   //32位  24 
int main(int argc, const char *argv[])
{

    printf("%d\n",sizeof(a));    
    printf("%d\n",sizeof(double));
    return 0;
}

例题:

  • #pragma pack(2) 意思是按照两字节对齐
  • 用空格隔开的是用于对齐的空白空间
struct data{
    char t1;    
    char t2;  
    unsigned short t3;  
    unsigned long t4;   
};  
1+1+2+4=8
1+1+2+  4  +8=16



struct data{
    char t1;  
    	      
    int t2;   
    short t3; 
};  
1+  3  +4+2+  2  =12
1+  3  +4+2+  2  =12



struct s1
{
	char c1;  
	int i;    
	char c2;  
    		  
}; 

1+  3  +4+1+  3  =12



//2字节对齐
struct s2
{
	char c1;  
	char c2;  
	int i;    
};

1+1+4=6



typedef struct Test
{
	short a;   
	struct
	{
		int b;  
		double c;  
		char d;  
	}p; 
	int e; 
}Test;4+  4  +8+1+  7  =242+  6  +24+4+  4  =40


#pragma pack(2)
typedef struct Test
{
	short a; 
	struct
	{
		int b; 
		double c[10]; 
		char d; 
	}p;
	int e; 
}Test;4+80+1+  1  =862+86+4=92


struct C{
    char  b;  
    		  
    int   a;  
    short c;  
};

1+  3  +4+2+  2  =12



struct C {  
    char a;  
    char b[3];  
    char c;  
};  

1+3+1=5



typedef struct
{
    int b;   
    char a;  
    long e;  
    char c;  
    float d; 
    double t; 
}node;  

4+1+  3  +8+1+  3  +4+8=24

小作业:

  1. 间接定义结构体数组,进行4种方式的定义和初始化
  2. 定义结构体存储10辆车(车的信息:品牌、单价、颜色)
    1. 定义函数,实现循环输入
    2. 定义函数,实现排序
    3. 定义函数,计算红色车的个数

我写的:
1.
在这里插入图片描述

2.在这里插入图片描述
代码如下:
1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct student
{
    char name[32];
    int age;
};

void out_struct(struct student *stu, int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("姓名:%s\t年龄:%d\n", (*(stu + i)).name, (*(stu + i)).age);
    }
}

int main(int argc, const char *argv[])
{
    // 按顺序初始化
    struct student stu1[2] = {{"张三", 12}, {"李四", 13}};

    // 不按顺序初始化
    struct student stu2[2] = {[1].age = 14, [0].name = "王五", [1].name = "赵六", [0].age = 16};

    // 单个初始化赋值
    struct student stu3[2];
    strcpy(stu3[0].name, "德玛西亚");
    stu3[0].age = 21;
    strcpy(stu3[1].name, "艾欧尼亚");
    stu3[1].age = 22;

    // 循环输入赋值
    struct student stu4[2];
    for (int i = 0; i < 2; i++)
    {
        scanf("%s", stu4[i].name);
        scanf("%d", &stu4[i].age);
    }
    out_struct(stu1,2);
    out_struct(stu2,2);
    out_struct(stu3,2);
    out_struct(stu4,2);

    return 0;
}

2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct car
{
    char pinpai[32];
    float danjia;
    char color[16];
} che;

void input_car(che *car);
void sort_car(che *car);
int calc_red_car(che *car);
void output_car(che *car);
void swap_car(che *car1, che *car2);

int main(int argc, const char *argv[])
{
    che car[10];
    input_car(car);
    sort_car(car);
    output_car(car);
    printf("红色车有 %d 辆\n", calc_red_car(car));
    return 0;
}

void input_car(che *car)
{
    for (int i = 0; i < 10; i++)
    {
        printf("第 %d 辆\n", i + 1);
        printf("请输入车的品牌:");
        scanf("%s", (*(car + i)).pinpai);
        printf("请输入车的单价:");
        scanf("%f", &(*(car + i)).danjia);
        printf("请输入车的颜色:");
        scanf("%s", (*(car + i)).color);
    }
}

void swap_car(che *car1, che *car2)
{
    che temp;
    // temp.danjia=(*car1).danjia;
    // strcpy(temp.color,(*car1).color);
    // strcpy(temp.pinpai,(*car1).pinpai);

    // (*car1).danjia=(*car2).danjia;
    // strcpy((*car1).color,(*car2).color);
    // strcpy((*car1).pinpai,(*car2).pinpai);

    // (*car2).danjia=temp.danjia;
    // strcpy((*car2).color,temp.color);
    // strcpy((*car2).pinpai,temp.pinpai);

    temp = *car1;
    *car1 = *car2;
    *car2 = temp;
}

//  价格升序
void sort_car(che *car)
{
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10 - i - 1; j++)
        {
            if ((*(car + j)).danjia > (*(car + j + 1)).danjia)
            {
                swap_car(car + j, car + j + 1);
            }
        }
    }
}

int calc_red_car(che *car)
{
    int num = 0;
    for (int i = 0; i < 10; i++)
    {
        if (!strcmp((*(car + i)).color, "红色"))
        {
            num++;
        }
    }
    return num;
}

void output_car(che *car)
{
    for (int i = 0; i < 10; i++)
    {
        printf("品牌:%s\t单价:%f\t颜色:%s\n",
               (*(car + i)).pinpai, (*(car + i)).danjia, (*(car + i)).color);
    }
}

思维导图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhk___

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值