指针:C语言的灵魂之刃(一)

文章一览

  • 前言
  • 一、指针的提出
  • 二、指针类型
    • 2.1 类型本质:内存访问的`「解码器」`
    • 2.2 void指针:万用钥匙的两面性
  • 三、指针的「七宗罪」(常见错误)
    • 3.1 野指针
    • 3.2 内存泄漏
    • 3.3 越界访问
    • 3.4类型双标
  • 四、指针的运算
    • 4.1 指针加减整数:内存导航仪
    • 4.2 指针相减:元素距离测量
    • 4.3 关系运算:内存地址排序
  • 总结

前言

指针是C语言的核心机制,本质上是存储内存地址的变量。每个变量在内存中都有唯一的地址,通过&运算符可获取该地址,而指针则像“地址簿”一样记录并管理这些位置信息。例如,int *p = &a;表示p存储了整型变量a的内存地址。

指针的核心能力体现在两个方面:直接访问内存和高效操作数据。通过运算符解引用(如p = 10;),可直接读写目标内存;通过指针算术(如p++),可遍历数组等连续内存结构。这种特性使指针成为实现动态内存分配(malloc/free)、构建链表/树等数据结构、优化函数参数传递的关键工具。

学习指针需要理解“地址-值”的双重性,掌握指针与数组、函数、结构体的交互规则。虽然可能面临野指针、内存泄漏等风险,但合理使用指针能大幅提升程序效率和灵活性,是突破初级编程迈向系统级开发的重要里程碑。

一、指针的提出

指针是一个重要的计算机科学概念,主要在编程语言中使用。它可以被视为一个变量,其值是指向另一个变量或函数内存地址的引用。
指针在C语言口中扮演着核心角色,它允许程序间接访问和操作内存中的数据。

int var = 42;      // 在内存0x1000位置存储42
int *ptr = &var;   // ptr的值是0x1000

指针声明时的符号是*, 但是这个*在不同的场景下,所表达的含义是不同的,具体如下:

星号(*)的三种身份:
声明时:int * 表示"指向整型的指针类型"
解引用时:*ptr 表示"取出该地址存储的值"
运算时:* 可作为乘号(但编译器能区分语境)

总结来说,指针就是一个变量,用来存放地址的变量

二、指针类型

指针的类型决定了指针可以指向的数据类型和步长(每次递增或递减的字节数)。例如,int类型的指针用于指向整型数据,每次递增或递减都会影响到相邻的整型数据。

2.1 类型本质:内存访问的「解码器」

int n = 0x12345678;
char *pc = (char*)&n;  // 按字节解析
int *pi = &n;          // 按4字节整型解析

核心作用:

  • 访问宽度:sizeof(*pointer)决定每次访问的字节数
  • 解码方式:指导编译器如何解释内存中的二进制数据
  • 指针运算:p+1的实际地址增量 = sizeof(指针类型)

2.2 void指针:万用钥匙的两面性

有一类特殊类型的指针(void*)

void *vp = malloc(100);  // 通用存储容器

这个类型是一个无类型指针,他不表示任何类型。
作为一个无类型指针它的优势是:

  • 可以作为泛型函数参数:如qsort(void *base, ...)
  • 用于实现内存操作函数:如memcpy(void* dest, ...)

但是在使用过程中一定要注意,作为无类型指针,无法直接参与运算(编译器不知道你要走多少),也不可以直接解引用(编译器不知道你要把多少内存拿出来)。
要想对无类型指针进行操作,需要进行强制类型转换:

float f = 3.14f;
void *vp = &f;
int *ip = (int*)vp;  // 强制重新解释二进制位

结果:

IEEE754浮点:0x4048f5c3
转换为int:1085488611
  • 硬件视角:CPU不关心类型,只按指令处理比特位

这时候发现不同类型进行指针转换会出现读取错误,这个错误与不同的数据在内存中的存储方式有关。

所以说在类型转换的时候要遵循一定的准则:
安全转换四原则

  1. 大小匹配检查:
assert(sizeof(Source) == sizeof(Dest));
  1. 对齐要求验证:
_Static_assert(_Alignof(int) <= _Alignof(float), "Alignment mismatch");
  1. 类型双关的正确方式:
int64_t num = 0x12345678;
double d;
memcpy(&d, &num, sizeof(d));  // 标准允许的复制方式
  1. 限定符传播规则:
const void *cvp = &x;
int *ip = (int*)cvp;  // 丢失const限定

三、指针的「七宗罪」(常见错误)

指针作为一个底层操作,它使得我们可以操作计算机内存,但是随之而来的便是一系列问题,对于内存的操作必须慎之又慎,否则将会引起严重的错误,接下来介绍指针的几种常见错误:

3.1 野指针

 int *p; *p = 5; (未初始化的指针)

未初始化的指针或非法指针使用可能导致程序崩溃。在使用指针前,必须确保它指向一个合法的内存地址。
野指针的成因通常有以下几种:

  • 指针未初始化
  • 指针越界访问
  • 指针指向的空间释放 (尽量不要返回临时变量的地址)

关于如何避免野指针,要注意几个方面:

  • 指针初始化 不知道初始化指向谁时,可以赋一个空指针 (NULL)
  • 小心指针越界
  • 指针指向空间释放即置为空
  • 使用指针之前检测指针的有效性

3.2 内存泄漏

malloc()后忘记free()

malloc是向堆空间申请一段内存,向堆申请的内存在没有手动释放时,不会自动释放,所以不及时释放可能会导致 栈溢出错误,在实际开发中可能会造成严重的系统奔溃。

3.3 越界访问

int arr[3]; *(arr+5) = 10;

由于指针操作,CPU不关心类型,只是按照bit位进行操作,这意味这系统不会进行类型安全检查,所以越界错误常有发生,实际编程中一定要注意这个错误。

3.4类型双标

float *p = (float*)&var;int变量强行按float解释)

除非确实有需要,否则将不同的类型指针进行转换极易操作数据读取的错误,得不到需要的结果。

四、指针的运算

4.1 指针加减整数:内存导航仪

int arr[5] = {10,20,30,40,50};
int *p = &arr[1];  // 指向20// 运算演示
p += 2;    // 现在指向40
p -= 3;    // 现在指向10(首元素)

指针的加减运算是与指针类型有关的,指针的类型决定了指针的步进距离:

char *cp = (char*)arr;
cp += 5;  // 移动5字节(可能跨越多个int元素)double *dp = (double*)arr;
dp += 1;  // 移动8字节(假设sizeof(double)=8)

同样的使用指针操作时要注意越界问题

4.2 指针相减:元素距离测量

int *start = &arr[0];
int *end = &arr[4];
printf("%d", start-end);
  • 得到的是指针之间得元素个数,小地址减大地址会得到负数。指针要指向同一块空间,否则会出现错误。
  • 指针运算时会自动关注指针大小,以类型大小为单位

4.3 关系运算:内存地址排序

标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个位置的指针比较
也就是说:
关键限制

  • 允许比较范围:arr[0] 到 arr[N](含结尾后的位置)
  • 禁止比较范围:arr[-1] 之前的位置
    合法操作
int arr[5];
int *p = arr;// 合法比较(结尾后位置)
while (p < arr+5) {  // 等价于 p <= arr+4*p++ = 0;
}// 合法指针运算
int *end = arr + 5;  // 指向结尾后位置
for (p=arr; p != end; p++) { ... }

非法操作

int arr[5];
int *p = arr - 1;  // 未定义行为// 错误比较(头前位置)
if (p > arr) { ... }  // 违反标准规定// 错误运算导致的崩溃
*p = 10;  // 可能触发段错误

总结

以上就是指针的开篇,讲了指针的定义、指针的常见错误、指针的类型以及指针的运算,后面会持续讲解指针与数组、多级指针以及指针的各种混合类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/39302.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

现代控制原理

一、在状态空间中&#xff0c;建立控制系统的数学模型 如&#xff1a;有单输入&#xff08;U)--单输出&#xff08;Y)控制系统&#xff0c;其状态方程和输出方程如下图&#xff1a; 二、画状态结构图 将上述状态方程转化为状态结构图有&#xff1a; 三、高阶控制系统的状态方…

【Git】基础使用

Git基础使用 基础配置工作区-暂存区-版本库添加文件修改文件版本回退撤销修改删除文件分支管理强制删除分支 基础配置 初始化仓库&#xff1a; git init # 此时就会生成一个 .git 的文件夹&#xff0c;切勿修改或删除文件夹里的内容配置仓库——名字&#xff1a; git config…

系统与网络安全------网络应用基础(2)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 交换机 认识交换机 交换机&#xff0c;Switch 用户将多台计算机/交换机连接在一起&#xff0c;组建网络 交换机负责为其中任意两台计算机提供独享线路进行通信 非网管型交换机 即插即用交换机 即插即用&…

【xiaozhi赎回之路-2:语音可以自己配置就是用GPT本地API】

固件作用 打通了网络和硬件的沟通 修改固件实现【改变连接到小智服务器的】 回答逻辑LLM自定义 自定义了Coze&#xff08;比较高级&#xff0c;自定义程度比较高&#xff0c;包括知识库&#xff0c;虚拟脚色-恋人-雅思老师-娃娃玩具{可能需要使用显卡对开源模型进行微调-产…

蓝桥杯——嵌入式学习日记

因为lED和LCD共用PC8~PC15引脚&#xff0c;要通过锁存&#xff08;LE&#xff09;和&#xff08;GPIOC->ODR&#xff09;来避免LED和LCD引脚冲突 修改点: main.c中&#xff0c;GPIO初始化引脚后&#xff0c;LE&#xff08;PD2引脚低电平锁存&#xff0c;退出透明模式&…

Liunx系统Microsoft SQL Server数据库还原

1.确认Linux系统已安装SQLServer服务并启动 2.在Windows中使用SSMS连接原数据库与Linux远程数据库服务 3.备份 成功备份如下 4.上传bak文件到远程Linux服务器 登陆Linux服务并创建数据库文件夹 退出Linux服务

【学习资源】多元时序分析问题和时序图模型

工业数据分析领域中&#xff0c;多元时序数据分析是一个广泛的问题。今天和大家简单介绍多元时序预测、聚类、分类、时序图模型和相应的深度学习库。 图片来源&#xff1a;https://www.researchgate.net/publication/349207209_Multivariate_Time-Series_Anomaly_Detection_via…

QT二 QT使用generate form 生成常用UI,各种UI控件

一 。没有使用general form 和 使用 general form 后&#xff0c;file层面和代码层面的不同比较 file层面的不同 代码层面的不同&#xff0c; 在 使用了general form之后&#xff0c;在主界面的构造方法中&#xff0c;使用ui->setupUi(this),就完成了所有UI的处理。 而之…

Haption力反馈遥操作机器人:6自由度高精度技术,定义远程操作新标准

Haption在力反馈遥操作机器人技术领域展现了强大的创新能力。其核心技术——力反馈技术&#xff0c;通过提供高度逼真的触觉反馈&#xff0c;显著提升了远程操作的精确性与用户体验。这种技术在工业、医疗等高要求场景中表现出色&#xff0c;同时也为科研和教育领域提供了有力支…

魔塔社区的torch_empty错误问题的解决办法

前言 我在运行魔塔社区&#xff08;modelscope&#xff09;的ZhipuAI/chatglm3-6b模型&#xff08;智谱&#xff09;的实例程序的时候&#xff0c;碰到了一个奇怪的错误&#xff08;torch.empty&#xff09;&#xff0c;我尝试解决了一下。 &#xff08;当前采用的Python版本…

全面了解 Cookies、Session 和 Token

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

51c自动驾驶~合集26

我自己的原文哦~ https://blog.51cto.com/whaosoft/11968755 #大模型/Sora/世界模型之间是什么关系 1 什么是大模型 人工智能大模型&#xff08;Artificial Intelligence Large Model&#xff0c;简称AI大模型&#xff09;是指具有庞大的参数规模和复杂程度的机器学习模…

分布式环境下的重复请求防护:非Redis锁替代方案全解析

目录 引言 方案一&#xff1a;前端防护策略 方案二&#xff1a;后端协同控制 方案三&#xff1a;流量控制与过滤 滑动窗口限流 布隆过滤器 方案四&#xff1a;基于框架的实践方案 多层防护策略与最佳实践 总结 引言 在Web应用开发中&#xff0c;防止用户重复点…

4.1 C#获取目录的3个方法的区别

C#中常用有如下3个获取目录的方式如下 1.Directory.GetCurrentDirectory():获取当前工作目录&#xff0c;工作目录可能被用户或其他代码修改。尽量少用。&#xff08;似乎只要在运行中使用另存为或者打开某个文件夹&#xff0c;当前工作目录就修改&#xff09; 2.Application…

【漏洞复现】Next.js中间件权限绕过漏洞 CVE-2025-29927

什么是Next.js&#xff1f; Next.js 是由 Vercel 开发的基于 React 的现代 Web 应用框架&#xff0c;具备前后端一体的开发能力&#xff0c;广泛用于开发 Server-side Rendering (SSR) 和静态站点生成&#xff08;SSG&#xff09;项目。Next.js 支持传统的 Node.js 模式和基于边…

MCU-芯片时钟与总线和定时器关系,举例QSPI

时钟源&#xff1a; 时钟源为系统时钟提供原始频率信号&#xff0c;系统时钟则通过&#xff08;分频、倍频、选择器&#xff09;成为整个芯片的“主时钟”&#xff0c;驱动 CPU 内核、总线&#xff08;AHB、APB&#xff09;及外设的运行。 内部时钟源&#xff1a; HSI&#x…

使用 ByteDance 的 UI-TARS Desktop 探索 AI 驱动的 GUI 自动化新前沿

文章目录 UI-TARS Desktop 是什么&#xff1f;技术亮点应用场景如何快速上手&#xff1f;与其他技术的对比未来展望结语 随着人工智能技术的快速发展&#xff0c;AI 正在从单纯的文本生成和图像识别迈向更复杂的交互场景。ByteDance 近期推出的 UI-TARS Desktop&#xff08;基于…

DockerFile制作镜像(Dockerfile Creates an Image)

DockerFile制作镜像 hub.docker.com 搜索到的 Redis官方镜像&#xff0c;提示我们可以创建自己的 DockerFile 来添加 redis.conf 文件&#xff1a; 于是&#xff0c;我准备进行首次 DockerFile 的制作尝试。 一、准备工作 1.1 下载 redis.conf 我的方案是从 GitHub 上下载 …

C++List模拟实现|细节|难点|易错点|全面解析|类型转换|

目录 1.模拟代码全部 2.四大块代码理解 1.最底层&#xff1a;ListNode部分 2.第二层&#xff1a;ListIterator部分 3.第三层&#xff1a;ReserveListIterator部分 4最终层&#xff1a;List 1.模拟代码全部 using namespace std; template<class T> struct ListNode …

如何让自动驾驶汽车“看清”世界?坐标映射与数据融合概述

在自动驾驶领域,多传感器融合技术是实现车辆环境感知和决策控制的关键。其中,坐标系映射和对应是多传感器融合的重要环节,它涉及到不同传感器数据在统一坐标系下的转换和匹配,以实现对车辆周围环境的准确感知。本文将介绍多传感器融合中坐标系映射和对应的数学基础和实际应…