嵌入式C语言:什么是指针?
目录
一、指针的基本概念
1.1. 定义指针
1.2. 赋值给指针
1.3. 解引用指针
1.4. 指针运算
1.5. 空指针
1.6. 函数参数
1.7. 数组和指针
1.8. 示例代码
二、指针在内存中的表示
2.1. 内存地址存储
2.2. 内存模型
2.3. 指针与硬件交互
2.4. 示例代码
三 、指针的重要性
3.1. 访问硬件寄存器
3.2. 中断服务程序(ISR)中的指针
3.3. 动态内存分配
3.4. 函数指针
3.5. 指针数组和数组指针
3.6. 指针与结构体
3.7. 优化性能
3.8. 安全性考虑
在嵌入式系统编程中,C语言指针的使用非常普遍且重要。指针允许直接访问和操作内存地址,在嵌入式系统中尤其关键,可以高效地管理有限的硬件资源。
一、指针的基本概念
在C语言中,指针是一种特殊的变量类型,它存储的是另一个变量的内存地址,而不是数据值本身。就像是一个指向内存中某个特定位置的 “路标”,这个位置可以是一个变量、一个数组元素或者是一个函数的入口地址。【C语言进阶】指针详解-CSDN博客
1.1. 定义指针
指针变量通过在其类型前加上星号(*
)来定义。意味着该变量将存储一个内存地址,而不是数据值。
int *ptr; // ptr 是一个指向 int 类型数据的指针
1.2. 赋值给指针
指针变量可以被赋予一个变量的地址。通常使用地址运算符(&
)来获取变量的地址。
int a = 5;
int *ptr = &a; // ptr 现在存储了变量 a 的内存地址
1.3. 解引用指针
使用星号(*
)作为解引用运算符来获取指针所指向的值。
int value = *ptr; // value 现在存储了 ptr 所指向的 int 类型的值,即变量 a 的值 5
1.4. 指针运算
指针可以进行加减运算,但这些运算不是基于字节,而是基于指针所指向的数据类型的大小。
int arr[10]; // 定义一个包含 10 个 int 类型元素的数组
int *p = arr; // p 指向数组的第一个元素
p++; // p 现在指向数组的第二个元素(因为 p 是指向 int 的指针,所以 +1 实际上跳过了 sizeof(int) 个字节)
1.5. 空指针
空指针(NULL
)是一个特殊的指针值,表示它不指向任何有效的内存地址。将指针初始化为 NULL
是一种常见的做法,以避免野指针问题。
int *p = NULL; // p 是一个空指针,不指向任何有效的内存地址
1.6. 函数参数
指针经常用作函数参数,以允许函数修改调用者的变量。
#include <stdio.h>void increment(int *x)
{(*x)++; // 修改指针所指向的变量的值
}int main() {int num = 5;increment(&num); // 传递 num 的地址给函数// num 现在等于 6printf("num = %d", num);return 0;
}
1.7. 数组和指针
数组名本身就是一个指向数组第一个元素的指针(常量指针),但数组名和指针之间也有一些重要的区别和注意事项。
#include <stdio.h>int main() {int arr[5] = {1, 2, 3, 4, 5};int *p = arr; // p 指向数组的第一个元素// 使用指针遍历数组for (int i = 0; i < 5; i++) {printf("%d ", *(p + i)); // 输出数组的每个元素}
}
1.8. 示例代码
以下是一个完整的示例代码,演示了上述所有指针的基本概念:
#include <stdio.h>void increment(int *x) {(*x)++;
}int main() {int a = 5;int *ptr = &a; // 定义指针并赋值printf("a 的值是: %d\n", a);printf("ptr 指向的值是: %d\n", *ptr);increment(ptr); // 通过指针修改 a 的值printf("调用 increment 后,a 的值是: %d\n", a);int arr[5] = {1, 2, 3, 4, 5};int *p = arr;printf("数组元素是:\n");for (int i = 0; i < 5; i++) {printf("%d ", *(p + i));}printf("\n");int *nullPtr = NULL; // 定义空指针if (nullPtr == NULL) {printf("nullPtr 是一个空指针\n");}return 0;
}
运行此代码将输出:
a 的值是: 5
ptr 指向的值是: 5
调用 increment 后,a 的值是: 6
数组元素是:
1 2 3 4 5
nullPtr 是一个空指针
二、指针在内存中的表示
2.1. 内存地址存储
指针变量本身占用一定的内存空间(通常是几个字节,取决于平台和编译器),用于存储另一个变量的内存地址。在32位系统中,指针通常占用4个字节;在64位系统中,指针通常占用8个字节。
2.2. 内存模型
在嵌入式系统中,内存通常分为几个不同的区域:代码区(存储程序代码)、数据区(包括全局变量和静态变量)、堆区(动态分配的内存)、栈区(用于函数调用和局部变量)。指针可以指向这些区域中的任何一个。嵌入式C语言:内存管理-CSDN博客
2.3. 指针与硬件交互
在嵌入式系统中,指针经常用于与硬件寄存器交互。硬件寄存器的地址是固定的,因此指针可以用来直接访问这些寄存器,从而控制硬件的行为。
2.4. 示例代码
以下是一个简单的嵌入式C语言示例,展示了指针的使用:
#include <stdint.h>
#include <stdio.h>// 假设有一个硬件寄存器的地址是 0x40021000
#define HARDWARE_REGISTER ((volatile uint32_t *)0x40021000)int main() {int a = 10;int *ptr = &a; // 定义一个指向 int 的指针,并指向变量 a// 使用指针访问和修改变量 a 的值printf("a 的原始值是: %d\n", a);*ptr = 20; // 通过指针修改 a 的值printf("通过指针修改后,a 的值是: %d\n", a);// 假设我们要设置硬件寄存器的值*HARDWARE_REGISTER = 0xDEADBEEF; // 直接访问硬件寄存器并设置其值// 注意:在实际嵌入式系统中,对硬件寄存器的访问可能需要特定的同步或保护机制,// 这里为了简化示例而省略了这些细节。return 0;
}
这里的volatile
关键字很重要,它告诉编译器这个变量(指针所指向的寄存器)可能会被硬件或其他异步因素改变,编译器不应该对涉及这个变量的操作进行优化。
代码中的 HARDWARE_REGISTER
宏定义仅用于说明目的,并不代表一个真实的硬件寄存器地址。在实际嵌入式系统中,需要查阅硬件手册来获取正确的寄存器地址。此外,对硬件寄存器的访问通常涉及到特定的内存映射和访问权限,这些都需要根据具体的硬件平台来处理。
在嵌入式系统中使用指针时,需要特别注意内存对齐、内存访问权限(如读/写权限)、以及硬件特定的限制。不正确的指针操作可能会导致程序崩溃、硬件损坏或不可预测的行为。
三 、指针的重要性
在嵌入式C语言编程中,指针的使用至关重要,它们不仅用于基本的内存访问和操作,还广泛用于与硬件交互、处理中断、管理数据结构以及优化性能。
3.1. 访问硬件寄存器
在嵌入式系统中,指针经常用于直接访问硬件寄存器的地址。由于硬件寄存器的地址是固定的,因此可以使用指向特定地址的指针来读写这些寄存器。允许程序员直接控制硬件的行为,如设置时钟频率、配置外设接口等。
3.2. 中断服务程序(ISR)中的指针
在嵌入式系统中,中断服务程序经常需要使用指针来访问全局变量或共享数据结构。这些指针通常在中断的上下文中注册,并在中断发生时被用来访问或修改数据。
volatile uint8_t *ledStatusPtr; // 全局变量指针,指向LED状态数组void ISR_ButtonPress(void) {// 假设ledStatusPtr已经在某处被正确初始化*ledStatusPtr ^= 0x01; // 切换LED状态(假设是单个LED)
}void initializeLEDStatusPointer(uint8_t *ledArray) {ledStatusPtr = ledArray; // 在中断服务程序外部初始化指针
}
3.3. 动态内存分配
尽管在资源受限的嵌入式系统中动态内存分配(如使用malloc
和free
)需要谨慎使用,但在某些情况下,它仍然是必要的。例如,当处理动态大小的数据结构(如链表、队列等)时,指针可以用于动态分配和释放内存。
#include <stdlib.h>typedef struct {int data;struct Node *next;
} Node;Node* createNode(int data) {Node *newNode = (Node *)malloc(sizeof(Node)); // 动态分配内存if (newNode != NULL) {newNode->data = data;newNode->next = NULL;}return newNode;
}void freeList(Node *head) {Node *temp;while (head != NULL) {temp = head;head = head->next;free(temp); // 释放内存}
}
3.4. 函数指针
函数指针在嵌入式系统中非常有用,特别是在实现回调机制、状态机或事件驱动架构时。它们允许程序员在运行时决定调用哪个函数,从而提供更大的灵活性和可配置性。
typedef void (*CallbackFunction)(int);void onDataReceived(int data) {// 处理接收到的数据...
}void registerCallback(CallbackFunction callback) {// 假设在某个地方保存了这个回调,以便稍后调用// 例如,在中断服务程序中callback(42); // 调用注册的回调
}int main() {registerCallback(onDataReceived); // 注册回调// 其他代码...return 0;
}
3.5. 指针数组和数组指针
在嵌入式系统中,处理大量数据时,指针数组和数组指针非常有用。指针数组允许存储多个变量的地址,而数组指针则指向一个数组的首地址。这些结构在处理多维数组、字符串数组或复杂数据结构时特别有用。【C语言】语义陷阱探秘(一):指针与数组-CSDN博客
#include <stdio.h>int main() {int arr[3] = {1, 2, 3};int *arrPtr[3] = {&arr[0], &arr[1], &arr[2]}; // 指针数组,存储数组元素的地址// 使用指针数组访问数组元素for (int i = 0; i < 3; i++) {printf("arrPtr[%d] = %d\n", i, *arrPtr[i]);}int (*ptrToArray)[3] = &arr; // 数组指针,指向整个数组printf("ptrToArray[1] = %d\n", (*ptrToArray)[1]); // 使用数组指针访问数组元素return 0;
}
3.6. 指针与结构体
在嵌入式系统中,结构体通常用于表示复杂的数据结构,如硬件配置、通信协议包等。指针常用于访问和修改结构体成员。
typedef struct {uint8_t address;uint8_t data;
} I2CPacket;void sendI2CPacket(I2CPacket *packet) {// 假设有一个发送I2C包的函数// 使用packet指针访问和发送地址和数据
}
3.7. 优化性能
在嵌入式系统中,性能通常是一个关键因素。通过使用指针,可以直接访问和修改内存中的数据,从而避免不必要的函数调用或数据复制。此外,指针还可以用于实现高效的算法和数据结构,如快速排序、哈希表等。
3.8. 安全性考虑
虽然下面的代码示例没有直接展示安全性考虑(如访问未初始化的指针、越界访问数组等),但强调了在使用指针时应该遵循的最佳实践。
// 初始化指针的最佳实践
int *safePtr = NULL;
int data = 10;
safePtr = &data; // 指向有效数据// 检查空指针
if (safePtr != NULL) {// 安全地使用指针...
}// 避免越界访问数组
int myArray[5];
for (int i = 0; i < 5; i++) {myArray[i] = i * 2; // 安全地访问数组元素
}
// 注意:不要访问 myArray[5] 或更高的索引,这是越界的。
总之,在嵌入式C语言编程中,指针是强大且灵活的工具。它们允许程序员直接访问和控制硬件、高效地管理内存和数据结构、实现回调和事件驱动机制等。然而,使用指针时也需要格外小心,以确保代码的安全性和可靠性。
相关文章:
嵌入式C语言:什么是指针?
目录 一、指针的基本概念 1.1. 定义指针 1.2. 赋值给指针 1.3. 解引用指针 1.4. 指针运算 1.5. 空指针 1.6. 函数参数 1.7. 数组和指针 1.8. 示例代码 二、指针在内存中的表示 2.1. 内存地址存储 2.2. 内存模型 2.3. 指针与硬件交互 2.4. 示例代码 三 、指针的重…...
解锁 KaiwuDB 数据库工程师,开启进阶之路
解锁 KaiwuDB 数据库工程师试题,开启进阶之路 一、KaiwuDB 数据库全方位洞察 (一)核心特性深度解析 原生分布式架构:摒弃传统集中式存储的局限,KaiwuDB 采用原生分布式架构,将数据分散存于多个节点。这不仅能有效避免单点故障风险,保障数据的高可用性,还能凭借并行处…...
ffmpeg7.0 aac转pcm
#pragma once #define __STDC_CONSTANT_MACROS #define _CRT_SECURE_NO_WARNINGSextern "C" { #include "libavcodec/avcodec.h" }//缓冲区大小(缓存5帧数据) #define AUDIO_INBUF_SIZE 40960 /*name depthu8 8s16 …...
【Pandas】pandas Series rdiv
Pandas2.2 Series Binary operator functions 方法描述Series.add()用于对两个 Series 进行逐元素加法运算Series.sub()用于对两个 Series 进行逐元素减法运算Series.mul()用于对两个 Series 进行逐元素乘法运算Series.div()用于对两个 Series 进行逐元素除法运算Series.true…...
线程安全问题介绍
文章目录 **什么是线程安全?****为什么会出现线程安全问题?****线程安全问题的常见场景****如何解决线程安全问题?**1. **使用锁**2. **使用线程安全的数据结构**3. **原子操作**4. **使用volatile关键字**5. **线程本地存储**6. **避免死锁*…...
为AI聊天工具添加一个知识系统 之27 支持边缘计算设备的资源存储库及管理器
本文问题 现在我们回到 ONE/TWO/TREE 的资源存储库 的设计--用来指导 足以 支持 本项目(为AI聊天工具增加一套知识系统)的 核心能力 “语言处理” 中 最高难度系数的“自然语言处理” 中最具挑战性的“含糊性” 问题的解决。--因为足以解决 自然语言中最…...
初识verilog HDL
为什么选择用Verilog HDL开发FPGA??? 硬件描述语言(Hardware Descriptipon Lagnuage,HDL)通过硬件的方式来产生与之对应的真实的硬件电路,最终实现所设计的预期功能,其设计方法与软件…...
VS2015 + OpenCV + OnnxRuntime-Cpp + YOLOv8 部署
近期有个工作需求是进行 YOLOv8 模型的 C 部署,部署环境如下 系统:WindowsIDE:VS2015语言:COpenCV 4.5.0OnnxRuntime 1.15.1 0. 预训练模型保存为 .onnx 格式 假设已经有使用 ultralytics 库训练并保存为 .pt 格式的 YOLOv8 模型…...
Notepad++上NppFTP插件的安装和使用教程
一、NppFTP插件下载 图示是已经安装好了插件。 在搜索框里面搜NppFTP,一般情况下,自带的下载地址容易下载失败。这里准备了一个下载连接:Release v0.29.10 ashkulz/NppFTP GitHub 这里我下载的是x86版本 下载好后在nodepad的插件里面选择打…...
Kotlin | Android Provider 的实现案例
目标 使用 Android Room 实现持久化库。 代码 Kotlin 代码编写 DemoDatabase,在build生成 DemoDatabase_Impl 疑问 Provider的数据会存在设备吗? 内部存储: 当使用 Room 创建数据库(如 DemoDatabase),数据库文件通常…...
频域自适应空洞卷积FADC详解
定义与原理 在探讨FADC的核心策略之前,我们需要深入了解其定义和工作原理。FADC是一种创新性的卷积技术,旨在克服传统空洞卷积的局限性。其核心思想是从 频谱分析的角度 改进空洞卷积,通过 动态调整膨胀率 来平衡有效带宽和感受野大小。 FADC的工作原理可以从以下几个方面…...
Edge浏览器内置的截长图功能
Edge浏览器内置截图功能 近年来,Edge浏览器不断更新和完善,也提供了长截图功能。在Edge中,只需点击右上角的“...”,然后选择“网页捕获”->“捕获整页”,即可实现长截图。这一功能的简单易用,使其成为…...
GAN的应用
5、GAN的应用 GANs是一个强大的生成模型,它可以使用随机向量生成逼真的样本。我们既不需要知道明确的真实数据分布,也不需要任何数学假设。这些优点使得GANs被广泛应用于图像处理、计算机视觉、序列数据等领域。上图是基于GANs的实际应用场景对不同G…...
Math Reference Notes: 希腊字母表
希腊字母(Greek alphabet)是古希腊语使用的字母系统,也是西方字母系统的先驱之一,广泛应用于现代数学、物理学、工程学以及各种科学领域。希腊字母有24个字母,它们分为大写和小写两种形式。 1. Alpha (Α, α) 发音&a…...
高通,联发科(MTK)等手机平台调优汇总
一、常见手机型号介绍: ISP除了用在安防行业,还有手机市场,以及目前新型的A/VR眼睛,机器3D视觉机器人,医疗内窥镜这些行业。 下面是一些最近几年发布的,,,旗舰SOC型号: 1.联发科:天玑92…...
Rust语言使用iced实现简单GUI页面
使用cargo新建一个rust项目 cargo new gui_demo cd gui_demo 编辑Cargo.toml文件 ,添加iced依赖 [package] name "gui_demo" version "0.1.0" edition "2021"[dependencies] iced "0.4.2" 编辑src/main.rs文件: u…...
使用wav2vec 2.0进行音位分类任务的研究总结
使用wav2vec 2.0进行音位分类任务的研究总结 原文名称: Using wav2vec 2.0 for phonetic classification tasks: methodological aspects 研究背景 自监督学习在语音中的应用 自监督学习在自动语音识别任务中表现出色,例如说话人识别和验证。变换器模型…...
25/1/11 嵌入式笔记<esp32> 初入esp32
用Arduino平台,学习了点亮led灯。 //定义LED引脚 int led_pin 12;void setup() {//设定引脚为输出模式pinMode(led_pin,OUTPUT):}void loop() {// 点亮LED:digitalWrite(led_pin,HIGH);//延时1sdelay(1000);//熄灭LEDdigitalWrite(led_pin,LOW)://延时…...
基于SMT32U575RIT单片机-中断练习
任务 查看手册对所有的拓展板上和相对应的底板的引脚对应的端口找到以下结论 通过STM32MX软件对各个引脚进行相应的配置 1.第一种切换模式电脑发送 #include "main.h" #include "icache.h" #include "usart.h" #include "gpio.h"/*…...
在Django的Serializer的列表数据中剔除指定元素
【Python工作随笔】 提问 如何在List序列化方法中剔除不要的元素,例如在成绩中剔除0 class BasicDescriptionSubjectBoxPlotSerializer(serializers.Serializer):语文 serializers.ListField(sourcescore_chinese)数学 serializers.ListField(sourcescore_math…...
我喜欢的数学题
偏向抖机灵性质的,考察理解的,而不是比拼计算量的,可能跟现在岁数大了算不明白了多少有点关系吧。 高高手,别太重计算,给普通孩子留条路。就算将来真的理工治国,也没必要都往人形计算机方面引导。毕竟你未来…...
Redis解决热key问题
当Redis遇到热key问题时,即某个或某些key被频繁访问,可能导致单个Redis节点负载过高,影响整个系统性能。以下是一些常见的解决方案: 1. 缓存预热与复制 缓存预热:在系统启动阶段,将热key对应的value预先加…...
【git】-2 分支管理
目录 一、分支的概念 二、查看、创建、切换分支 1、查看分支-git branch 2、创建分支- git branch 分支名 3、切换分支- git checkout 分支名 三、git指针 -实现分支和版本间的切换 四、普通合并分支 git merge 文件名 五、冲突分支合并 【git】-初始gi…...
Win11+WLS Ubuntu 鸿蒙开发环境搭建(二)
参考文章 penHarmony南向开发笔记(一)开发环境搭建 OpenHarmony(鸿蒙南向开发)——标准系统移植指南(一) OpenHarmony(鸿蒙南向开发)——小型系统芯片移植指南(二&…...
Meilisearch ASP.Net Core API 功能demo
安装 MeiliSearch 0.15.5 0.15.5demo code using Meilisearch; using System.Data; using System.Text.Json; using System.Text.Json.Serialization;namespace MeiliSearchAPI {public class MeilisearchHelper{public MeilisearchHelper(){DefaultClient…...
程序员独立开发竞品分析:确定网站使用什么建站系统
要确定一个网站使用的建站系统,可以通过以下几种方法尝试分析: 查看页面源代码: 打开网站,右键点击页面并选择“查看页面源代码”。在代码中查找一些常见的建站系统标志,例如: WordPress 的迹象:…...
selenium+pyqt5自动化工具总结
说明:本工具是,操作外部google浏览器、selenium是无法操作qt界面中嵌套的浏览器的, 工具在后面 1. 代码结构 pycharm打开的文件下,再写一个子文件,文件导入的时候把子文件名带上 这样就可以在 外层使用命令 pyinst…...
docker GPU安装
docker 离线安装 docker下载地址:https://download.docker.com/linux/static/stable/x86_64/ 解压: tar xzvf docker-24.0.6.tgz移动解压后的内容 sudo mv docker/* /usr/local/bin/创建 docker.service配置文件 sudo vim /etc/systemd/system/dock…...
hutool糊涂工具通过注解设置excel宽度
import java.lang.annotation.*;Documented Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) public interface ExcelStyle {int width() default 0; }/*** 聊天记录*/ Data public class DialogContentInfo {/**…...
Three.js教程015:全面讲解Three.js的UV与应用
文章目录 全面讲解UV与应用UV介绍代码演示完整代码全面讲解UV与应用 UV介绍 在 Three.js 中,UV 坐标(也称为纹理坐标)是用来定义纹理如何映射到三维模型上的一组二维坐标。UV 坐标的范围通常是 (0, 0) 到 (1, 1),其中: U 对应纹理的横向轴(类似于 X 轴)。V 对应纹理的…...
内容管理系统做网站/网络营销推广实训报告
前情提要: 2021 年进了基科班CSP2021, 思考了半小时想到了一个绝妙的A题做法,半小时rush完,半小时对拍。然后觉得 B 是个傻逼题,连写三种 dp 全假,最后只剩了不到一个小时,边抓头发边rush暴力&…...
汕头免费做网站/网站收录批量查询
“双循环”新发展格局下,稳产业链和供应链成为我国经济社会发展的战略基石。商业银行在国家推进数字中国战略和加快产业基础高级化、产业链现代化的战略关键期,应该而且能够乘势而上,探索数字化的深层次内涵,融入产业供应链金融生…...
网站建设与维护 电子版/产品运营方案
转自:blog.leanote.com/post/afanti.denggmail.com/b5f4f526490b ROI Align 是在Mask-RCNN这篇论文里提出的一种区域特征聚集方式, 很好地解决了ROI Pooling操作中两次量化造成的区域不匹配(mis-alignment)的问题。实验显示,在检测测任务中将 ROI Poolin…...
linux主机 安装wordpress/深圳网络营销策划公司
同步自:http://www.blogjava.net/AndersLin/archive/2006/06/27/55421.html 上次发了一文《软件企业的绩效管理和内部消耗》讨论很多,"置身珠海,学习与奋斗"cnBlogs让我认识其实我说了那么多都只是绩效的保健性&…...
企业网站组网方案/成都网站seo
and then interview#$%^&* again转载于:https://www.cnblogs.com/rexhost/archive/2005/07/08/188977.html...
政府网站规划/今天特大军事新闻
手动卸载基于 AutoCAD 产品的文件,从而删除所有残留文件。 清理安装失败所遗留的文件。 一、解决方案: 通过“控制面板”卸载该程序。 删除以下位置残留的 AutoCAD 文件夹: C:\Program Files\Autodesk C:\ProgramData\Autodesk C:\Users\All …...