当前位置: 首页 > news >正文

嵌入式入门Day17

Day17

  • 指针
    • 指向堆区的指针
      • 练习
    • 野指针
  • 预处理指令
    • 预处理指令的作用
    • 预处理指令的分类
    • 文件包含指令
    • 宏定义
    • 条件编译

在这里插入图片描述

指针

指向堆区的指针

  1. 堆区空间的申请和释放(mallocfree
#include <stdlib.h>void *malloc(size_t size)/** 		功能:在堆区申请指定的size字节大小空间,并将该空间的地址返回给调用函数* 		参数:要申请的空间大小,以字节为单位* 		返回值:成功返回申请出来的空间的起始地址,是一个万能指针,使用是需要转变为具体的指针,失败时返回NULL*/void free(void *ptr)/**		功能:将堆区的手动申请的内存空间释放*		参数:堆区空间的指针*		返回值:无*/
  1. 可以完成单个空间的申请和释放也可以完成连续空间的申请和释放
    • 单个空间
#include<stdio.h>
#include<stdlib.h>         //标准的库函数头文件int main(int argc, const char *argv[])
{//申请一个字节的空间char *ptr1 = (char *)malloc(1); //malloc(sizeof(char))      //在堆区申请1字节的空间,并将地址赋值给ptr1if(ptr1 == NULL){printf("空间申请失败\n");return -1;}//程序执行至此,表示空间已经申请成功printf("*ptr1 = %d, *ptr1 = %c\n", *ptr1, *ptr1);  //没有初始化,默认是随机值*ptr1 = 'H';       //给堆区空间进行赋值printf("*ptr1 = %d, *ptr1 = %c\n", *ptr1, *ptr1);  //没有初始化,默认是随机值//单个申请4字节int *ptr2 = (int *)malloc(sizeof(int));   //malloc(4)if(NULL == ptr2){printf("空间申请失败\n");return -1;}printf("*ptr2 = %d\n", *ptr2);       //随机值*ptr2 = 520;          //使用堆区空间printf("*ptr2 = %d\n", *ptr2);       //520//手动将其释放free(ptr1);free(ptr2);ptr1 = NULL;ptr2  = NULL;return 0;
}
- 连续空间
#include<stdio.h>
#include<stdlib.h>int main(int argc, const char *argv[])
{//连续申请5个字节的空间char *ptr1 = (char *)malloc(5);   //malloc(sizeof(char)*5)if(NULL == ptr1){printf("空间申请失败\n");return -1;}for(int i=0; i<5; i++){printf("%c\t", ptr1[i]);}printf("\n");//对堆区空间的使用ptr1[0] = 'A';ptr1[1] = 'B';ptr1[2] = 'C';ptr1[3] = '\0';printf("ptr1 = %s\n", ptr1);//申请连续的整形空间int *ptr2 = (int *)malloc(sizeof(int)*5);if(NULL==ptr2){printf("空间申请失败\n");return -1;}for(int i=0; i<5; i++){printf("请输入第%d个元素:", i+1);scanf("%d", ptr2+i);   //&ptr2[i]}printf("您输入的元素分别是:");for(int i=0; i<5; i++){printf("%d\t", ptr2[i]);}return 0;
}

练习

在堆区申请8个int类型的空间,存储8名学生的成绩,并完成相关操作
1> 申请空间
2> 释放空间
3> 录入学生成绩
4> 输出学生成绩
5> 进行从大到小排序
每个功能要求使用函数实现

#include <stdlib.h>
#include <string.h>//在堆区申请一个n字节大小的空间
int *shenqing(int n)
{//申请堆区变量int *score = (int *)malloc(sizeof(int)*n);//判断是否申请成功if (NULL == score){printf("申请空间失败\n");return NULL;}//下面两行是两种将堆区空间置零的方式memset(score, 0, sizeof(int)*n);bzero(score, sizeof(int)*n);return score;
}
//在堆区空间录入数据
void luru(int *score)
{//检测堆区指针是否有所指向if (NULL == score){printf("录入失败\n");return ;}else{//循环录入for (int i = 0; i < 8; i++){printf("请输入第%d个学生的成绩\n", i+1);scanf("%d", score+i);}printf("录入成功\n");}
}
//数据显示
void show(int *score)
{//检测指针是否有所指向if (NULL == score){printf("输出失败\n");}else{//遍历输出for (int i = 0; i < 8; i++){printf("%d\t", score[i]);}putchar(10);}
}
//冒泡排序
void sort(int *score)
{for (int i = 1; i < 8; i++){for (int j = 0; j < 8-i; j++){if (score[j] < score[j+1]){int temp = score[j];score[j] = score[j+1];score[j+1] = temp;}}}
}
//释放堆区空间
void myfree(int **score)
{	//调用free释放堆区空间free(*score);//将指针指向NULL防止出现野指针*score = NULL;
}int main(int argc, const char *argv[])
{int *score = shenqing(8);if (NULL == score){return -1;}luru(score);show(score);sort(score);show(score);myfree(&score);show(score);return 0;
}

野指针

  1. 野指针就是指向非法内存空间地址的指针
  2. 种类:
    • 定义指针变量时,没有初始化
    • 当指针指向数组时,数组下标越界后的指针
    • 指针原本有指向,但是指向的内存过期了(生命周期结束、手动释放)—> 悬空指针
    • 指针函数返回的局部变量的地址
	1. int *ptr;		//未初始化2.	int arr[5] = {1, 2, 3, 4, 5};int *qtr = arr;qtr += 5; 		//野指针3. 	int *ptr = NULL;if(1){int num = 520;ptr = &num;}//if结束后num将会被释放,ptr将会成为野指针4.	int *ptr = (int *)malloc(4);free(ptr);//释放后ptr成为野指针5. 	int *fun(){int num = 520;return &num;}int main(void){int *ptr = fun();//此处ptr也为野指针}
  1. 如何预防野指针:当内存过期后,将指针置空即可

预处理指令

预处理指令的作用

在程序执行之前做做一些简单的前置工作,例如条件编译、宏定义、文件包含等等

预处理指令的分类

  1. 文件包含:#include
  2. 宏定义:有参宏和无参宏,使用一个名字代替字符串
  3. 条件编译:根据给定的条件,判断某些程序是否进行编译

文件包含指令

  1. 使用格式:#include <文件名.h> 或者 #include "文件名.h"
  2. 区别:
    • 使用尖括号包裹的文件,直接在库文件中寻找该文件 /usr/include/
    • 使用双引号包裹的文件,会先从当前文件位置寻找目标文件
  3. 作用:就是将包含的文件内容预处理阶段展开
  4. 使用文件包含完成分文件编译
    • 头文件(.h文件):主要用于函数的声明、结构体的声明、宏定义、全局变量的定义
    • 源文件(.c文件):实现函数的定义
    • 主程序文件(包含main函数的.c文件):用于执行程序
    • 一般而言,同一个文件的头文件和源文件名字一致

头文件格式

#ifndef 头文件名_H
#define 头文件名_H	//以上两行是用于防止重复包含的,当这个宏名没有被使用是才会编译下面的声明,使用过后就不会再编译了函数声明...#endif

源文件格式

#include <stdio.h>
#include "头文件名.h"函数实现...

主函数格式

#include <stdio.h>
#include "头文件名.h"int main(void)
{函数调用...return 0;
}

最终编译时进行联合编译即可
gcc head.c main.c

宏定义

  1. 在C语言中,可以使用一个名字表示一个字符串或一类字符串,该名字是一个常量
    例如:生活中的圆周率π,表示的就是3.1415926… 圆周率表示就是一个宏
  2. 宏的作用仅仅是指代作用,只做直接替换,不进行任何操作,也不做任何检查
  3. 使用格式:
    • 无参宏:#define 宏名 字符串
    • 有参泓:#define 宏名(形参列表) 包含形参列表的字符串
    • 注意:有参宏定义中,形参没有数据类型,直接给定变量名即可
  4. 取消宏定义:#undef 宏名
#include <stdio.h>
#define PI 3.14
#define SUM(x,y) x+yint main(void)
{double num = PI;printf("num = %.2lf\n", num);int a = 4;int b = 5;printf("a + b = %d\n", SUM(a, b));		//此处输出为:a + b = 9printf("%d\n",5*SUM(a, b));		//注意!!!宏定义只进行简单的替换操作,不进行任何计算//替换结果为:5*4+5,将会先计算乘法,结果为25,而不是45//平时使用时请务必加上括号,以免出现意外情况
}
  1. 有参宏和函数的区别
    • 展开时机:有参宏而言,在预处理阶段展开,而函数在调用时才展开
    • 内存使用:有参宏而言,占用的是所在函数的空间,而函数在调用时会单独开辟空间
    • 效率上:有参宏的效率高于函数
    • 有参宏是宏定义,只替换不计算不做任何正确性检查,而函数调用要符合函数的调用标准,形参会进行计算

条件编译

  1. 根据给定的条件,对某些程序代码进行编译
  2. 判断宏是否存在
#ifdef 宏名
代码块1;
#else
语句块2;
#endif

如果宏名被定义过了,就编译代码块1,反之编译代码块2

#ifndef 宏名
代码块1;
#else
代码块2;
#endif

如何宏名没有被定义,就编译代码块1,反之编译代码块2
3. 判断表达式是否成立

#if 表达式1
代码块1;
#elif 表达式2
代码块2;
#else
代码块3;
#endif

先判断表达式1,成立编译代码块1,否则判断表达式2,表达式2成立就编译代码块2,否则编译代码块3


http://www.mrgr.cn/news/78083.html

相关文章:

  • 填补覆盖空白,小型机器人让智能清洁再“净”一步!
  • Linux 上查看和转换 .bin 文件为二进制格式
  • C# 数据结构之【图】C#图
  • Java基于Spring Boot框架的房屋租赁系统,附源码
  • 图形渲染性能优化
  • 《Python基础》之数据容器
  • 【数据结构】链表的基本操作
  • Tkinter置顶弹窗提示操作成功
  • 分布式搜索引擎Elasticsearch(一)
  • Maven学习笔记
  • 设计模式——抽象工厂模式
  • 报表工具功能对比:免费易上手的山海鲸报表 vs 庞大用户群体的Tableau
  • [论文阅读-综述]Supervised Speech Separation Based on Deep Learning: An Overview
  • Android 应用测试的各种环境问题记录(Instrumentation测试)
  • [UE5学习] 一、使用源代码安装UE5.4
  • Dockerfile构建报错【ERROR: failed to solve: process】的解决办法
  • ES更新问题 Failed to close the XContentBuilder异常
  • 动态链接库工作原理 PLT GOT
  • 【数据挖掘】一、基于LDA的用户兴趣建模(兴趣标签生成模型)--用户兴趣挖掘模型
  • 《硬件架构的艺术》笔记(七):处理字节顺序
  • 车载显示display基础知识和评估
  • 02.02、返回倒数第 k 个节点
  • 如何制作项目网页
  • 【C++11】尽显锋芒
  • 指针测试总结(一)(一维数组)
  • CTF-RE 从0到 N: 高版本 APK 调试 + APK逻辑修改再打包 + os层调试[2024 强网杯青少年专项赛 Flip_over] writeup