【C语言指针精讲】从内存到运算,吃透指针核心逻辑

🏠个人主页:黎雁

🎬作者简介:C/C++/JAVA后端开发学习者

❄️个人专栏:C语言数据结构(C语言)EasyX游戏规划

✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

    • 一、先搞透:内存和地址是啥?💾
    • [二、指针变量:存地址的专属容器 📦](#二、指针变量:存地址的专属容器 📦)
      • [1. 取地址操作符 `&`:把地址揪出来](#1. 取地址操作符 &:把地址揪出来)
      • [2. 定义指针变量:专门存地址的变量](#2. 定义指针变量:专门存地址的变量)
      • [3. 解引用操作符 `*`:通过地址操作变量 🔍](#3. 解引用操作符 *:通过地址操作变量 🔍)
    • [三、指针变量的小秘密:类型和大小 🤫](#三、指针变量的小秘密:类型和大小 🤫)
      • [1. 指针变量的大小:只和系统有关!](#1. 指针变量的大小:只和系统有关!)
      • [2. 指针类型的意义:决定操作范围 📏](#2. 指针类型的意义:决定操作范围 📏)
        • [① 解引用时操作多少字节](#① 解引用时操作多少字节)
        • [② 指针移动时跳多少步](#② 指针移动时跳多少步)
      • [3. `void*`指针:万能的地址垃圾桶 🗑️](#3. void*指针:万能的地址垃圾桶 🗑️)
    • [四、指针运算:让指针动起来 ⚡](#四、指针运算:让指针动起来 ⚡)
      • [1. 指针 ± 整数:遍历数组超方便](#1. 指针 ± 整数:遍历数组超方便)
      • [2. 指针 - 指针:算元素个数](#2. 指针 - 指针:算元素个数)
      • [3. 指针的关系运算:比较地址大小](#3. 指针的关系运算:比较地址大小)
    • [写在最后 📝](#写在最后 📝)

✨指针是C语言的灵魂,也是学好底层编程的关键~ 这篇会从内存底层 讲到指针实战,保证你看完能从懵变懂!

一、先搞透:内存和地址是啥?💾

要学指针,得先明白内存是怎么干活的------

内存是电脑的临时仓库🏠,程序运行时的所有数据(变量、数组、函数等)都存在这里。

它被切成了很多1字节大小的小格子(内存单元),每个格子能存8个二进制位(bit),相当于一个8位的小抽屉🧩。

为了能快速找到这些抽屉,系统给每个内存单元分配了一个唯一编号 ------这就是地址 ,也叫指针!(划重点:地址 = 指针 ✍️)

举个例子:内存像一排带编号的储物柜,每个柜子对应一个内存单元,柜子的编号就是地址。你报编号(地址),就能精准找到对应的柜子(内存单元)~

二、指针变量:存地址的专属容器 📦

知道了地址是啥,那怎么在代码里用它?这就需要指针变量

1. 取地址操作符 &:把地址揪出来

想拿到变量的地址,用&就行~ 比如定义一个int a = 10

c 复制代码
#include <stdio.h>
int main() {
    int a = 10;  // 向内存申请4个连续字节,存数值10
    // 用&a取a的地址,%p是打印地址的格式符
    printf("a的地址:%p\n", &a); 
    return 0;
}

注意⚠️:int类型占4字节,但&a拿到的是这4个字节里最小的那个字节的地址(也就是起始地址)~

2. 定义指针变量:专门存地址的变量

地址得找个容器存起来,这个容器就是指针变量 !定义格式是类型* 变量名

  • 类型:表示指针指向的变量的类型 (比如int*就是指向int变量的指针);
  • *:表示这个变量是指针变量。

示例:

c 复制代码
#include <stdio.h>
int main() {
    int a = 10;
    // 定义int类型的指针变量pa,存a的地址
    int* pa = &a;
    return 0;
}

可以理解为:pa是个地址标签🏷️,贴在了a的内存单元上,通过pa就能找到a~

3. 解引用操作符 *:通过地址操作变量 🔍

存地址不是目的,目的是通过地址操作变量的值 !这就要用*(解引用):

c 复制代码
#include <stdio.h>
int main() {
    int a = 10;
    int* pa = &a;
    
    // *pa 表示pa指向的变量,这里就是a,输出10
    printf("通过pa访问a:%d\n", *pa);
    *pa = 20;  // 通过pa修改a的值
    printf("修改后的a:%d\n", a);  // 输出20
    return 0;
}

简单说:*是通过地址找变量的钥匙🔑,用*指针变量就能直接读写目标变量~

三、指针变量的小秘密:类型和大小 🤫

1. 指针变量的大小:只和系统有关!

不管指针指向啥类型,它的大小只由系统的位数决定:

  • 32位系统:地址是32个bit → 占4字节;
  • 64位系统:地址是64个bit → 占8字节。

看代码验证(64位系统下):

c 复制代码
#include <stdio.h>
int main() {
    int* pa;   // 指向int的指针
    char* pc;  // 指向char的指针
    // 输出都是8(64位系统)
    printf("int*的大小:%zd\n", sizeof(pa));
    printf("char*的大小:%zd\n", sizeof(pc));
    return 0;
}

2. 指针类型的意义:决定操作范围 📏

指针的类型不是摆设!它决定了两件事:

① 解引用时操作多少字节

比如int*char*指向同一个int变量:

c 复制代码
#include <stdio.h>
int main() {
    // a占4字节,内存中是0x11 0x22 0x33 0x44
    int a = 0x11223344;
    int* pa = &a;
    char* pc = &a;
    
    *pa = 0;  // int*解引用:操作4字节 → a变成0x00000000
    // *pc = 0; // char*解引用:只操作1字节 → a变成0x11223300
    return 0;
}
② 指针移动时跳多少步

指针±整数时,偏移量由指针类型决定:

c 复制代码
#include <stdio.h>
int main() {
    int a = 10;
    int* pa = &a;
    char* pc = &a;
    
    printf("pa+1的地址:%p\n", pa + 1); // int*跳4字节
    printf("pc+1的地址:%p\n", pc + 1); // char*跳1字节
    return 0;
}

总结:指针类型决定了操作的粒度,不能随便混用~

3. void*指针:万能的地址垃圾桶 🗑️

void*是无类型指针,可以存任意类型变量的地址 ,但有个缺点:不能直接解引用/做运算,得先强制类型转换

c 复制代码
#include <stdio.h>
int main() {
    int a = 10;
    void* pv = &a;  // 存int的地址
    // *pv = 20; // 直接用会报错!
    *(int*)pv = 20; // 强制转成int*,就能正常用啦
    return 0;
}

void*常用在函数参数里(比如实现泛型),后面会详细讲~

四、指针运算:让指针动起来 ⚡

指针不是死地址,它能做运算!常用的有3种:

1. 指针 ± 整数:遍历数组超方便

数组在内存中是连续存储的,用指针±整数可以直接遍历元素:

c 复制代码
#include <stdio.h>
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int* p = &arr[0];  // 指向数组第一个元素
    for (int i = 0; i < 5; i++) {
        // p+i 指向第i个元素,*(p+i)就是元素值
        printf("%d ", *(p + i)); 
    }
    return 0; // 输出:1 2 3 4 5
}

也能反向遍历(从最后一个元素开始):

c 复制代码
int* p = &arr[4];  // 指向最后一个元素
for (int i = 0; i < 5; i++) {
    printf("%d ", *(p - i)); // 输出:5 4 3 2 1
}

2. 指针 - 指针:算元素个数

前提:两个指针必须指向同一块连续内存(比如同一个数组),结果是两个指针之间的元素个数:

c 复制代码
#include <stdio.h>
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    // 输出4(下标4和下标0之间的元素个数)
    printf("元素个数:%d\n", &arr[4] - &arr[0]);
    return 0;
}

经典用法:模拟实现strlen(统计字符串长度):

c 复制代码
#include <stdio.h>
// 自己写的strlen
size_t my_strlen(char* p) {
    char* q = p;
    while (*q != '\0') { // 找到字符串结束符'\0'
        q++;
    }
    return q - p; // 指针差=字符个数
}

int main() {
    char str[] = "hello";
    printf("字符串长度:%zd\n", my_strlen(str)); // 输出5
    return 0;
}

3. 指针的关系运算:比较地址大小

指针可以用>、<、>=、<=比较地址,常用在遍历连续内存:

c 复制代码
#include <stdio.h>
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int* p = &arr[0];
    // 当p没超过数组末尾地址时,继续循环
    while (p < &arr[5]) { 
        printf("%d ", *p);
        p++;
    }
    return 0; // 输出:1 2 3 4 5
}

写在最后 📝

指针其实没那么难,核心逻辑就3句话:

  1. 地址就是指针,是内存单元的唯一编号;
  2. 指针变量是存地址的容器,用来间接操作目标变量;
  3. 解引用和指针运算,是指针实现高效操作的关键。

多写代码+调试(比如打印地址、观察指针移动),很快就能摸清指针的脾气~ 下一篇会讲继续讲解呀,记得来看哦🥰!

相关推荐
秦苒&2 小时前
【C语言指针四】数组指针变量、二维数组传参本质、函数指针变量、函数指针数组
c语言·开发语言·c++·c#
秋雨雁南飞2 小时前
C# 字符串占位
开发语言·c#
拾贰_C2 小时前
[Python | pytorch | torchvision ] models like ResNet... 命名变量说明
开发语言·pytorch·python
傅里叶的耶2 小时前
C++ Primer Plus(第6版):第三章 处理数据
开发语言·c++
CC.GG2 小时前
【C++】AVL树
java·开发语言·c++
CoderCodingNo2 小时前
【GESP】C++四级真题 luogu-B4416 [GESP202509 四级] 最长连续段
开发语言·c++·算法
a程序小傲2 小时前
京东Java面试被问:Fork/Join框架的使用场景
java·开发语言·后端·postgresql·面试·职场和发展
⑩-2 小时前
Java四种线程创建方式
java·开发语言
月光在发光2 小时前
22_GDB调试记录(未完成)
java·开发语言