news 2026/4/16 22:41:09

C语言指针入门到理解:一篇文章系统梳理指针核心知识(1)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言指针入门到理解:一篇文章系统梳理指针核心知识(1)

C语言指针入门到理解:一篇文章系统梳理指针核心知识(1)

当我们初学C的时候,一看到指针就容易紧张。
其实指针难的地方,不在语法,而在于它把“变量”这层东西,进一步推进到了“内存”这一层。

这篇文章就带你从内存方面出发,一起梳理一下指针相关的核心知识点,包括一下内容:

  • 指针到底是什么
  • &*分别在做什么
  • 为什么指针要有类型
  • const修饰指针怎么区分
  • 什么是野指针,怎么规避
  • 为什么函数想修改外部变量时要用指针

一、什么是指针

想理解指针,先要理解内存和地址

程序运行时,变量都会放在内存中,而内存会被划分为一个个内存单元。每个内存单元都有编号,这个编号就是地址。CPU 正是通过地址去找到对应的数据。

所以从本质上说:

指针就是地址,指针变量就是专门用来存地址的变量。

你可以把内存理解成一栋宿舍楼:

  • 每个房间就是一个内存单元
  • 每个房间号就是地址
  • 知道房间号,才能准确找到房间

在 C 语言里,这个“房间号”就对应指针。


二、取地址操作符&

定义一个变量时,本质上是在内存中申请空间,比如:

inta=10;

如果我们想知道变量a在内存中的位置,就要使用取地址操作符&

inta=10;printf("%p\n",&a);

这里的&a表示“取出变量a的地址”。

需要注意的是,a可能占多个字节,但&a取到的是这块空间起始位置的地址,也就是较小地址那个字节的位置。


三、指针变量是什么

地址也是数据,既然是数据,就可以存起来。
存地址的变量,就是指针变量。

例如:

inta=10;int*pa=&a;

这里pa就是一个指针变量,它里面存的是a的地址。指针变量本质上也是变量,只不过它存储的是地址

这句代码可以拆开理解:

  • pa是变量名
  • *表示pa是指针变量
  • int表示pa指向的是一个int类型的对象

也就是说,int *pa的真实含义是:

pa是一个指向整型数据的指针。


四、解引用操作符*

有了地址之后,下一步就是:怎么通过地址访问对应的数据?

答案就是使用解引用操作符*

inta=100;int*pa=&a;*pa=0;

这里的*pa表示:根据pa中存放的地址,找到对应的那块内存空间。

因为pa里存的是a的地址,所以*pa实际上就是a本身。
因此*pa = 0;最终修改的就是a

所以要分清:

  • pa是地址
  • *pa是地址对应的内容

五、指针为什么要有类型

很多人会问:
既然指针里存的都是地址,那为什么还要区分int*char*double*

原因很简单:

指针类型决定了编译器如何看待这块地址。

它主要影响两件事。

1. 决定解引用时访问几个字节

例如:

intn=0x11223344;int*pi=&n;char*pc=(char*)&n;
  • *piint处理,通常一次访问 4 个字节
  • *pcchar处理,一次只访问 1 个字节

这说明,指针类型决定了解引用权限。

2. 决定指针加减的步长

char*pc=(char*)&n;int*pi=&n;pc+1;// 向后移动1个字节pi+1;// 向后移动1个int大小

所以:

  • char* + 1跳过 1 字节
  • int* + 1通常跳过 4 字节

指针加 1,不是简单数值加 1,而是跨过一个对应类型的元素。


六、指针变量的大小

很多同学以为char*小一点,double*大一点,其实不是。(注意,这里是说的指针变脸本身的大小)

指针变量的大小,和它指向什么类型无关,只和平台有关。

一般来说:

  • 32 位平台下,指针大小是 4 字节
  • 64 位平台下,指针大小是 8 字节

所以在同一平台下,你如果运行下面的代码:

sizeof(char*)sizeof(int*)sizeof(double*)

通常结果都是一样的。


七、void*指针

void*可以理解成“泛型指针”。

它的特点是:

  • 可以接收任意类型的地址
  • 常用于函数参数,增强通用性
  • 但不能直接解引用
  • 也不能直接进行指针加减运算

void*它适合在函数参数中接收不同类型的数据地址,从而实现一定程度的泛型效果,但本身缺少具体类型信息,因此不能直接参与解引用和常规指针运算。

例如:

void*p=&a;

这样写可以,但不能直接写:

*p=10;p+1;

真正使用前,需要先转换成具体类型。

比如说

void*p=&a;(int*)p=10;//强制类型转换为int* 类型的指针p+1;

八、const修饰指针怎么理解

这是初学者特别容易混淆的内容。

1.const int *p

constint*p=&n;

含义是:

  • p可以改指向
  • 但不能通过p修改它指向的内容

也就是:

指向的内容不能改,指针本身能改。


2.int * const p

int*constp=&n;

含义是:

  • p不能再指向别处
  • 但可以通过p修改内容

也就是:

指针本身不能改,指向的内容能改。


3.const int * const p

两边都限制:

  • 不能改指向
  • 也不能通过它改内容

4. 一个简单记忆方法

const限制谁:

  • const*左边,限制的是“指向的内容”
  • const*右边,限制的是“指针本身”

九、指针运算

指针常见的运算主要有三种:

  • 指针+/-整数
  • 指针 - 指针
  • 指针关系运算

1. 指针+整数

常用于遍历数组:

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

这里p + i表示移动到第i个元素的位置。


2. 指针 - 指针

常用于计算两个位置之间相差多少个元素。

例如模拟strlen

intmy_strlen(char*s){char*p=s;while(*p!='\0')p++;returnp-s;}

这里p - s表示字符串长度。


3. 指针关系运算

例如:

while(p<arr+sz){printf("%d ",*p);p++;}

通过比较指针位置来判断遍历是否结束。


十、什么是野指针

野指针是指针里最危险的问题之一。

野指针定义是:

野指针就是指针指向的位置不可知,或者是不正确、没有明确限制的位置。

简单理解就是:
这个指针已经不知道自己到底在指向哪里了,但你还试图通过它访问内存,那很容易造成各种各样的错误。


十一、野指针的常见成因

1. 指针未初始化

int*p;*p=20;

p里是随机值,直接解引用非常危险。

2. 指针越界访问

intarr[10]={0};int*p=arr;for(inti=0;i<=11;i++){*(p++)=i;}

一旦越过数组合法范围,p就成了野指针。

3. 返回局部变量地址

int*test(){intn=100;return&n;}

函数结束后,局部变量生命周期结束,这个地址就失效了。


十二、如何规避野指针

1. 初始化指针

如果一开始不知道该指向谁,就先赋值为NULL

int*p=NULL;

NULL是一个值为 0 的标识符常量,表示空地址。


2. 不要越界访问

指针只能访问合法申请到的那块内存,不能超范围操作。


3. 不再使用时及时置空

p=NULL;

这样做可以减少误用风险。


4. 使用前检查合法性

if(p!=NULL){// 再使用}

这是非常常见的安全写法。


十三、assert断言的作用

在使用指针前,经常会先做合法性校验:

assert(p!=NULL);

assert的作用是:如果条件为真,程序继续执行;如果条件为假,程序直接报错终止,并指出问题位置,便于调试。

例如:

intmy_strlen(constchar*str){assert(str);intcount=0;while(*str){count++;str++;}returncount;}

这相当于在函数开头加了一道保护。


十四、指针最重要的应用:传址调用

为什么要学指针?
因为有些问题,单纯传值是解决不了的。

例如交换两个变量:

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

这样写交换失败,因为这是传值调用,函数里改的是形参副本,不是原变量。

正确写法应该是传地址:

voidSwap2(int*px,int*py){inttmp=*px;*px=*py;*py=tmp;}

调用时:

Swap2(&a,&b);

这时传入的是ab的地址,函数内部通过解引用直接修改原变量,所以交换成功。

这也是指针最核心的价值之一:

当函数需要修改主调函数中的变量时,就要用传址调用。


十五、总结

学指针,不能只背语法,主要是你得明白这样一条链路:

变量 -> 内存空间 -> 地址 -> 指针变量 -> 解引用访问

你把它理顺了,自然就会明白:

  • 为什么要用&
  • 为什么要有*
  • 为什么指针类型很重要
  • 为什么会有野指针
  • 为什么传地址才能改外部变量

ps:各位读完文字后可以思考一下这几个问题哦,读者自证不难(其实答案我上面都算是写出来了)


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

生成式AI应用上线前必做的7项合规审计:避开监管雷区与幻觉失控危机

第一章&#xff1a;生成式AI应用服务治理方案 2026奇点智能技术大会(https://ml-summit.org) 生成式AI应用服务的规模化落地正面临模型行为不可控、输出合规性难保障、服务链路缺乏可观测性等核心挑战。治理方案需覆盖模型接入、请求路由、内容审核、响应重写、审计留痕与策略…

作者头像 李华
网站建设 2026/4/16 22:40:50

SQL如何实现实时数据的滑动窗口分析_SQL性能调优

滑动窗口卡住因窗口未对齐实时节奏、PARTITION BY与ORDER BY顺序颠倒、RANGE BETWEEN缺时间索引&#xff1b;实时分析须用ROWS BETWEEN&#xff0c;ORDER BY event_time ASC且event_time需索引&#xff1b;LAG()须显式定义窗口帧&#xff1b;MySQL中ROW_NUMBER()需联合索引优化…

作者头像 李华
网站建设 2026/4/16 22:40:46

Windows下Delft3D安装全攻略:从申请许可到编译运行(避坑指南)

Windows下Delft3D安装全攻略&#xff1a;从申请许可到编译运行&#xff08;避坑指南&#xff09; Delft3D作为水动力学模拟领域的标杆工具&#xff0c;其强大的泥沙输运预测和地貌演变分析能力&#xff0c;让无数水利工程师又爱又恨——尤其是在Windows系统下的安装过程&#x…

作者头像 李华
网站建设 2026/4/16 22:40:36

Spring Cloud 2027 边缘计算支持:构建分布式边缘系统

Spring Cloud 2027 边缘计算支持&#xff1a;构建分布式边缘系统 1. 边缘计算的核心概念 边缘计算是一种分布式计算范式&#xff0c;它将计算和数据存储移近数据源&#xff0c;减少延迟并提高响应速度。Spring Cloud 2027 为边缘计算提供了全面的支持&#xff0c;使开发者能够构…

作者头像 李华
网站建设 2026/4/16 22:40:29

【SRE×GenAI双认证实践】:基于127个真实故障案例提炼的6层容错架构——从API网关到推理引擎全链路兜底方案

第一章&#xff1a;生成式AI应用容错设计原则的演进与范式跃迁 2026奇点智能技术大会(https://ml-summit.org) 早期生成式AI系统多沿用传统服务容错范式——以冗余部署、超时熔断和重试机制为核心&#xff0c;但这类策略在面对模型输出不可控性&#xff08;如幻觉、语义漂移、…

作者头像 李华
网站建设 2026/4/16 22:40:15

2026奇点大会AI健身计划技术栈深度解析:Transformer+生物力学仿真+边缘端实时肌电反馈(仅限首批注册开发者获取)

第一章&#xff1a;2026奇点智能技术大会&#xff1a;AI健身计划 2026奇点智能技术大会(https://ml-summit.org) 在2026奇点智能技术大会上&#xff0c;“AI健身计划”作为核心落地项目首次向全球开发者开源。该计划融合多模态感知、实时生物信号建模与个性化强化学习策略&am…

作者头像 李华