news 2026/5/2 7:09:52

【C语言指针从入门到精通:保姆级教程(1)】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C语言指针从入门到精通:保姆级教程(1)】

指针

  • 1. 前言
    • 1.1 什么是指针?
    • 1.2 为什么人们常说,指针是C语言的灵魂?
  • 2. 指针变量和地址
    • 2.1 取地址操作符(&)
    • 2.2 指针变量和解引用操作符(*)
      • 2.2.1 指针变量
      • 2.2.2 如何拆解指针类型
      • 2.2.3 解引用操作符
    • 2.3 指针变量的大小
  • 3. 指针变量类型的意义
    • 3.1 指针的解引用
    • 3.2 指针+-整数
    • 3.3 void* 指针
  • 4. 指针运算
    • 4.1 指针+-整数
    • 4.2 指针-指针

关于指针的这篇博客我会把它拆分成5篇博客来细讲,请耐心看完。

1. 前言

在学习C语言的过程中,几乎所有的人都会遇到一个困难且无法避免的知识点,但同样它也是每一位学习C语言的人所需跨越的点——指针

1.1 什么是指针?

在C语言中有一句最本质的话:指针,就是内存地址。
内存里的每一个字节,都有一个唯一的编号,这个编号就是地址。
而用来存放这个地址的变量,我们将它称为指针变量,简称指针

可以用一个生活中的例子来解释上面的话:

  • 内存就像是一家酒店。
  • 每个房间都是一块内存空间。
  • 门牌号就是内存地址。
  • 记录门牌号的纸张就是指针。
    当每个房间都有了房间号,就可以快速的找到房间。

如果把上面的例子对照到计算机中,又是怎么样呢?
我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放在内存中,那我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何高效的管理呢?
其实也是把内存划分为一个个的内存单元,每个内存单元的大小取一个字节。
一个比特位可以储存一个2进制位的1或0

bit-比特位1Byte=8bit Byte-字节1KB=1024Byte KB1MB=1024KB MB1GB=1024MB GB1TB=1024GB TB1PB=1024TB PB

其中,每个内存单元,相当于一个学生宿舍一个字节空间里面能放8个bit位,就好比同学们住的8人间,每个人是一个比特位。

每个内存单元也都有一个编号(这个编号就相当于·宿舍房间的门牌号),有了这个内存单元的编号,CPU就可以快速找到一个内存空间。

生活中我们把门牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语言中给地址起了新的名字叫:指针

所以我们可以理解为:
内存单元的编号 == 地址 == 指针

1.2 为什么人们常说,指针是C语言的灵魂?

很多人都说指针是C语言的灵魂,这究竟是为什么呢?
核心原因在于:指针让C语言拥有了直接操作内存的能力,同时赋予了语言极高的灵活性、效率底层控制力,这是C语言区别于很多高级语言的关键特性。
主要有以下几个好处

  • 运行效率高,传参、处理数据开销小
  • 能实现动态内存分配,灵活管理空间
  • 是链表、树等复杂数据结构的基础
  • 可以做底层开发(系统、驱动)
  • 让C语言语法更灵活、功能更强大

2. 指针变量和地址

2.1 取地址操作符(&)

理解了指针和地址的关系,我们再回到C语言,在C语言中创建变量其实就是向内存申请空间,比如:

#include<stdio.h>intmain(){inta=10;return0;}

比如,上述的代码就是创建了整型变量a,内存中申请4个字节,用于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:

0x00F9FA80 0x00F9FA81 0x00F9FA82 0x00F9FA83

那我们如何取出a的地址呢?
这里就得学习一个操作符(&)-取地址操作符

#include<stdio.h>intmain(){inta=10;&a;//取出a的地址printf("%p\n",&a);return0;}

&a取出的是a所占4个字节中地址较小的地址。
虽然整形变量占4个字节,我们只要知道了第一个字节地址,顺藤摸瓜访问到4个字节的数据也是可行的。

2.2 指针变量和解引用操作符(*)

2.2.1 指针变量

那我们通过取地址操作符(&)拿到的地址是一个数值,比如0x006FFD60,这个数值有时候也是需要存储起来,方便后期再使用的,那我们把这样的地址值储存在哪里呢?答案是:指针变量中。
比如:

#include<stdio.h>intmain(){inta=10;int*p=&a;//取出a的地址并存储在指针变量p中return0;}

指针变量也是一种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。

2.2.2 如何拆解指针类型

我们看到p的类型是int*,我们该如何理解指针的类型呢?

inta=10;int*pa=&a;

这里的pa左边写的是int*,(*)是在说明pa是指针变量,而前面的int是说明pa指向的是整型(int)类型的对象

那如果有一个char类型的变量ch,ch的地址,要放在什么类型的指针变量呢?

charch='c';char*pc=&ch;

2.2.3 解引用操作符

我们将地址保存起来,未来是要使用的,那怎么使用呢?

在现实生活中,我们使用地址要找到一个房间,在房间里可以拿去或者存放物品。

C语言中其实也是一样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这里我们必须学习一个操作符叫:解引用操作符(*)。

#include<stdio.h>intmain(){inta=10;int*pa=&a;printf("%d\n",*pa);*pa=100;printf("%d\n",*pa);return0;}

上面的代码就运用到了解引用操作符,*pa的意思就是通过pa存放的地址,找到指向的空间,*pa其实就是a变量了;所以*pa = 100这个操作其实就等价于a = 100。

有同学肯定在想,这里如果目的只是想让a变成100的话,写成a = 100不就完了吗?为什么非要使用指针呢?
其实这里是把a的修改交给了pa来操作,这样对a的修改,就多了一种的途径,写代码就会更加灵活,后期慢慢就能理解了。

2.3 指针变量的大小

**在不同的机器中指针的大小是不同的。**比如:
在32位机器中,一个地址的大小是4个字节。但是在64位机器中,一个地址的大小是8个字节。

X86环境输出结果 X64环境输出结果

结论:

  • 32位平台下地址是32个bit位,指针变量大小是4个字节
  • 64位平台下地址是64个bit位,指针变量大小是8个字节
  • 注意指针变量的大小和类型是无关的,只要是指针类型的变量,在相同平台下,大小都是相同的。

3. 指针变量类型的意义

指针变量的大小和类型无关,只要是指针变量,在同一个平台下,大小都是一样的,为什么还要有各种各样的指针类型呢?

其实指针类型是有特殊意义的,我们接下来继续学习。

3.1 指针的解引用

对比下面两段代码,主要在调试时观察内存的变化。

//代码1#include<stdio.h>intmain(){intn=0x11223344;int*pi=&n;*pi=0;return0;}
//代码2#include<stdio.h>intmain(){intn=0x11223344;char*pc=(char*)&n;*pc=0;return0;}

调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第一个字节改为0。
结论:指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。
比如:char*的指针解引用就只能访问一个字节,而int*的指针的解引用就能访问4个字节。

3.2 指针±整数

先看一段代码,调试观察地址的变化。

#include<stdio.h>intmain(){intn=10;char*pc=(char*)&n;int*pi=&n;printf("&n = %p\n",&n);printf("pc = %p\n",pc);printf("pc+1 = %p\n",pc+1);printf("pi = %p\n",pi);printf("pi+1 = %p\n",pi+1);return0;}

代码运行的结果如下:

我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。指针可以+1,那也可以-1。

结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

3.3 void* 指针

在指针类型中有一种特殊的类型是 void* 类型的指针变量,可以理解为无具体类型的指针(或者叫泛型指针),这种类型的指针可以用来接受任意类型地址。但是也有局限性,void*类型的指针不能直接进行±整数和解引用的运算。

举例:

#include<stdio.h>intmain(){inta=10;int*pa=&a;char*pc=&a;return0;}

在上面的代码中,将一个int类型的变量的地址赋值给一个char*类型的指针变量。编译器给出了一个警告(如下图),是因为类型不兼容。而使用void*类型就不会有这样的问题。

使用void*类型的指针接收地址:

#include<stdio.h>intmain(){inta=10;void*pa=&a;void*pc=&a;*pa=10;*pc=0;return0;}

VS编译代码的结果:

那么我们可以看到,void*类型的指针可以接收不同类型的地址,但是无法直接进行指针运算。

那么void*类型的指针到底有什么用呢?
一般void*类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。使得一个函数来处理多种类型的数据。

4. 指针运算

指针的基本运算有三种,分别是:

  • 指针±整数
  • 指针-指针
  • 指针的运算关系

4.1 指针±整数

因为数组在内存中是连续存放的,只要知道第一个元素的地址,顺藤摸瓜就能找到后面的所有元素。

intarr[10]={1,2,3,4,5,6,7,8,910};

数组元素和下标
#include<stdio.h>intmain(){intarr[10]={1,2,3,4,5,6,7,8,9,10};int*p=&arr[0];inti=0;intsz=sizeof(arr)/sizeof(arr[0]);for(i=0;i<sz;i++){printf("%d ",*(p+i));}return0;}

4.2 指针-指针

指针-指针得出的结果会是什么呢?是之间的字节数吗?
不是,是两个指针之间相差的元素个数,而不是字节数。

#include<stdio.h>My_strlen(char*p){char*s=p;while(*s!='\0'){s++;}returns-p;}intmain(){printf("%d\n",My_strlen("abcdef"));return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/2 7:07:23

Nginx RPM 包下载指南(不安装)-003篇

文章目录 ✅ 方法一:使用 `yum install --downloadonly`(最常用、推荐) ▶ 命令格式: ▶ 示例:只下载 `nginx` 包(不安装) ⚠️ 注意事项: ✅ 方法二:使用 `yumdownloader`(来自 `yum-utils`,更灵活) ▶ 安装工具(若未安装): ▶ 下载单个包(不含依赖): ▶ 下…

作者头像 李华
网站建设 2026/5/2 7:04:54

JAVA陪玩小程序源码uniapp代码

项目结构 uniapp项目通常包含以下核心目录&#xff1a; pages/ └── index/ ├── index.vue // 首页 └── detail.vue // 陪玩详情页 components/ └── player-card.vue // 陪玩卡片组件 static/ └── icons/ // 图标资源 App.vue // 应用入口 main.js // 项目配置…

作者头像 李华
网站建设 2026/5/2 7:01:26

前端光标动画:从原理到实现,打造高性能交互体验

1. 项目概述&#xff1a;当光标成为画布上的舞者在数字交互的世界里&#xff0c;我们早已习惯了那个千篇一律的箭头或小手图标。它沉默、机械&#xff0c;仅仅是一个功能性的指示器。但有没有想过&#xff0c;这个最基础的交互元素&#xff0c;也能成为表达创意、传递情绪、甚至…

作者头像 李华
网站建设 2026/5/2 6:55:25

qapyq:AI模型训练数据集的图像管理与标注工作站实战指南

1. 项目概述&#xff1a;一个为AI模型训练而生的图像管理与标注工作站 如果你正在为Stable Diffusion、LoRA或者任何生成式AI模型准备训练数据集&#xff0c;那你一定体会过那种在成千上万张图片和文本标签之间反复横跳的痛苦。传统的看图软件和文本编辑器在这种高强度、高精度…

作者头像 李华