news 2026/9/3 23:23:44

C/C++指针核心知识回顾总结(一)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C/C++指针核心知识回顾总结(一)

C/C++指针核心知识总结:从内存地址到安全传参

一、先建立一个核心模型:指针就是地址值的载体

计算机内存通常按字节编址,每个字节单元都有唯一编号,这个编号就是地址。C 语言中的指针变量,就是专门保存地址的变量。

&用于取地址,*用于解引用:通过指针保存的地址找到目标对象。

#include<stdio.h>intmain(void){inta=10;int*p=&a;printf("a = %d\n",a);printf("address = %p\n",(void*)&a);printf("through pointer = %d\n",*p);*p=20;printf("a = %d\n",a);return0;}

注意:打印地址应使用%p,并将地址转换为void *。地址的具体数值由平台和运行时决定,不应写死。

二、指针大小和指针类型不是一回事

在常见平台上,32 位程序的指针通常占 4 字节,64 位程序的指针通常占 8 字节。同一平台上,char *int *double *通常等宽,但指针类型依然非常重要:它决定编译器如何解释目标内存。

1. 指针类型决定解引用尺度

char *解引用通常访问 1 个字节,int *解引用则按int的大小访问。也就是说,类型决定了一次读写的“权限范围”。

2. 指针加减按元素步进

p + 1不是地址数值简单加 1,而是向后移动一个所指向元素的大小:

  • char *通常前进 1 字节;
  • int *通常前进sizeof(int)字节;
  • double *通常前进sizeof(double)字节。

数组连续存储,因此可以用指针遍历数组:

intarr[]={1,2,3,4,5};intsize=(int)(sizeof(arr)/sizeof(arr[0]));for(int*p=arr;p<arr+size;++p){printf("%d ",*p);}

两个指针相减表示它们之间相差多少个元素,通常用于计算数组区间长度。arr + size是尾后指针,可以比较,不能解引用。

三、void *:通用地址,但不是万能指针

void *可以接收任意对象类型的地址,常用于通用接口。在 C 中它不能直接解引用,也不能直接按元素进行指针运算,使用前要转换回正确的类型。

voidprint_int(constvoid*data){constint*p=(constint*)data;printf("%d\n",*p);}

在 C++ 中,建议使用更明确的类型设计和显式转换,不要把void *当作类型安全的替代品。

四、const修饰指针:左管内容,右管指针

声明约束对象允许的操作
int *p无 const 限制可改*p,可改p
const int *p指向的内容不可通过p改内容,可改p的指向
int *const p指针变量本身可改内容,不可改p的指向
const int *const p内容和指针两者都不能改

阅读技巧:先找到变量名,再向两侧看。const*左边,约束目标内容;在*右边,约束指针变量。

不要通过错误的指针类型去修改const对象。修改 const 对象会产生未定义行为,不能把“编译器暂时没报错”当成正确性证明。

五、野指针:最常见的三个来源

  1. 局部指针未初始化:int *p;中的p可能是随机值。
  2. 指针越界:走出数组或动态内存的有效边界后还继续读写。
  3. 指向的空间已经失效:例如返回局部变量地址,函数结束后局部对象的生命周期已经结束。
int*bad_function(void){intlocal=100;return&local;// 错误:返回失效的局部地址}

安全习惯:能确定指向哪里就立即初始化为地址;不知道目标时初始化为NULL;使用前检查有效性;不再使用时可置空;严格控制数组边界和对象生命周期。C++ 代码优先使用nullptr

六、assert:把“必须成立”的条件写出来

assert定义在assert.h中。条件为真时继续,条件为假时输出错误信息并终止程序。它适合在 Debug 版本中捕捉程序员错误。

#include<assert.h>#include<stddef.h>size_tmy_strlen(constchar*str){assert(str!=NULL);size_tcount=0;while(*str!='\0'){++count;++str;}returncount;}

定义NDEBUG后,assert通常会被禁用。因此不要把有副作用的业务逻辑放进 assert 表达式。

七、传值调用与传址调用

C 的参数传递是传值。下面的函数只交换了形参副本:

voidswap_wrong(intx,inty){inttmp=x;x=y;y=tmp;}

如果函数需要修改调用者中的变量,就传入地址:

voidswap_int(int*px,int*py){inttmp=*px;*px=*py;*py=tmp;}intmain(void){inta=3;intb=5;swap_int(&a,&b);}

传值适合只依赖输入值的计算;传址适合需要修改调用者对象或避免复制较大对象的场景。

八、最后的四问自检法

每次看到*p,先问:

  1. p是否初始化?
  2. p是否为NULL/nullptr
  3. p指向的对象是否仍在有效生命周期内?
  4. 指针是否落在合法边界内,并且目标空间足够大?

把这四个问题变成习惯,指针代码的稳定性会明显提高。

编译建议

GCC 可以开启警告和调试信息:

gcc-Wall-Wextra-gpointer_demo.c-opointer_demo

警告不是错误的替代品,但它能尽早暴露类型不匹配、未使用变量和可疑表达式。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 5:05:34

数据库索引原理与优化实践:从B+树到慢SQL调优

索引这玩意儿&#xff0c;算是数据库里最容易被“背出来”又最容易被“问倒”的知识点。面试前能背得动聚簇索引、二级索引、最左前缀、覆盖索引&#xff0c;可真到线上遇到一条慢SQL&#xff0c;很多人却说不出它到底为什么没走索引。今天我把索引的底子拆开讲&#xff0c;用最…

作者头像 李华
网站建设 2026/9/2 8:33:00

JVM 知识整理

JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。虚拟机是指通过软件模拟的具有完整硬件功能的、运行在⼀个完全隔离的环境中的完整计算机系统。真实的操作系统中&#xff0c;对于进程的地址空间是进行了分区域的设计&#xff0c;JVM也就仿照了操作系统的情况…

作者头像 李华
网站建设 2026/9/1 7:13:01

2026专科毕业论文AI工具怎么选?七款横评

专科毕业论文的写作压力&#xff0c;经历过的人都有体会。选题被导师驳回两次、查重率卡在30%线上、格式调了整晚还是不对——这些场景对2026届毕业生来说并不陌生。市面上号称能解决这些问题的AI工具越来越多&#xff0c;宣传话术一个比一个漂亮&#xff0c;实际用起来差距却不…

作者头像 李华
网站建设 2026/9/2 10:19:49

LiteOS‑M再辨析:启动第一个任务,到底需不需要SVC?

——STM32MP157 M4核移植复盘&#xff0c;为什么原生HalStartToRun不用SVC也能完成PendSV调度系列&#xff1a;LiteOS‑M移植&#xff08;WindowsGCCMakefile&#xff0c;无Keil&#xff09; 前置阅读&#xff1a;《LiteOS‑M PendSV阻塞修复&#xff1a;从绕开调度器到SVCPend…

作者头像 李华
网站建设 2026/9/1 7:42:36

饲料破碎机设计全流程:SolidWorks建模与AutoCAD出图实战解析

简介&#xff1a;机械设计是工程领域的核心学科&#xff0c;毕业设计则是检验学生综合应用能力的关键环节。在中小型机械产品设计中&#xff0c;三维建模与二维工程图的协同表达至关重要。SolidWorks作为主流三维设计工具&#xff0c;能够高效完成参数化建模与装配干涉检查&…

作者头像 李华
网站建设 2026/9/3 19:01:04

LIS2DH12加速度计实战:寄存器配置、低功耗与运动检测应用

一块小到能放进指尖的芯片&#xff0c;却能感知你走路时每一个细微的步态、设备跌落前的瞬间姿态&#xff0c;甚至能听出塑料水管里那一声几乎听不见的泄漏振动。这就是 LIS2DH12&#xff0c;意法半导体&#xff08;ST&#xff09;在 MEMS 加速度计家族里非常能打的一位兄弟。如…

作者头像 李华