指针的初认识

指针是 C/C++ 等编程语言的核心概念,本质是存储内存地址的变量。通过指针,我们可以间接操作内存中的数据,实现灵活的内存管理、函数传参、动态数据结构(如链表、树)等功能。

一、核心概念:内存地址与指针变量
  1. 内存地址:计算机内存被划分为一个个字节的存储单元,每个单元有唯一的编号(即地址),类似酒店房间号。例如:变量 int a = 10; 会占用 4 个字节(int 默认 4 字节),系统会为这 4 个字节分配一个起始地址(如 0x7ffeefbff5c4)。

  2. 指针变量:专门用来存储内存地址的变量,其类型对应指向数据的类型(需匹配,否则可能导致内存访问错误)。语法:数据类型 *指针变量名;

二、指针的基本使用
1. 定义与初始化

c

运行

#include <stdio.h>

int main() {
    // 普通变量
    int a = 10;
    // 指针变量p:存储a的地址(&为取地址符)
    int *p = &a;

    printf("变量a的值:%d\n", a);        // 输出:10
    printf("变量a的地址:%p\n", &a);    // 输出:0x7ffeefbff5c4(示例地址)
    printf("指针p的值(a的地址):%p\n", p);  // 输出:0x7ffeefbff5c4
    printf("指针p指向的值(*p解引用):%d\n", *p);  // 输出:10

    return 0;
}
  • &a:取变量 a 的地址(地址运算符)。
  • *p:解引用运算符,通过指针 p 访问其指向的内存中的数据。
2. 指针修改目标变量的值

指针的核心用途之一:通过指针间接修改变量值(即使变量在不同作用域)。

c

运行

#include <stdio.h>

void changeValue(int *p) {
    // 修改指针指向的内存值
    *p = 20;
}

int main() {
    int a = 10;
    printf("修改前:%d\n", a);  // 10
    // 传递a的地址给指针参数
    changeValue(&a);
    printf("修改后:%d\n", a);  // 20
    return 0;
}
三、指针的关键特性
1. 指针的大小

指针的大小由操作系统位数决定(与指向的数据类型无关):

  • 32 位系统:指针占 4 字节(地址范围 0~2³²-1)。
  • 64 位系统:指针占 8 字节(地址范围 0~2⁶⁴-1)。

验证代码:

c

运行

#include <stdio.h>

int main() {
    int *p1;
    char *p2;
    double *p3;
    // 64位系统下均输出8
    printf("int*大小:%zu\n", sizeof(p1));
    printf("char*大小:%zu\n", sizeof(p2));
    printf("double*大小:%zu\n", sizeof(p3));
    return 0;
}
2. 空指针(NULL)

表示指针不指向任何有效的内存地址,避免野指针(指向随机地址的指针)导致的崩溃。

c

运行

// 定义空指针(C11后推荐用NULL或nullptr(C++))
int *p = NULL;
// 判空后再使用
if (p != NULL) {
    *p = 10; // 不会执行,避免访问非法内存
}
3. 野指针(危险!)
  • 定义:未初始化、指向已释放内存、越界的指针。
  • 示例(禁止运行):

    c

    运行

    // 未初始化的野指针
    int *p;
    // 访问随机地址,可能导致程序崩溃/内存破坏
    *p = 10;
    
  • 避免:
    1. 指针定义时初始化为 NULL;
    2. 动态分配内存后检查是否成功;
    3. 释放内存后将指针置为 NULL。
四、常见指针类型
1. 普通指针(指向基本类型)

c

运行

char ch = 'a';
char *pc = &ch;

double d = 3.14;
double *pd = &d;
2. 数组指针(指向数组)

数组名本质是数组首元素的地址(常量,不可修改):

c

运行

int arr[5] = {1,2,3,4,5};
// p指向数组首元素(等价于p = arr)
int *p = &arr[0];

// 通过指针遍历数组
for (int i = 0; i < 5; i++) {
    // 等价于*(p+i) 或 arr[i]
    printf("%d ", p[i]);
}
3. 函数指针(指向函数)

用于回调函数、动态调用函数:

c

运行

// 定义函数
int add(int a, int b) {
    return a + b;
}

// 定义函数指针(格式:返回值类型 (*指针名)(参数列表))
int (*fp)(int, int) = add;

// 调用:输出3
printf("%d\n", fp(1,2));
4. 二级指针(指向指针的指针)

用于修改指针本身(如动态分配内存、函数传指针的指针):

c

运行

int a = 10;
// 一级指针:指向a
int *p = &a;
// 二级指针:指向p
int **pp = &p;

// 输出:10(等价于*p 或 a)
printf("%d\n", **pp);
五、指针的核心应用场景
  1. 函数传参(修改外部变量):如交换两个整数(必须用指针,否则仅修改形参):

    c

    运行

    void swap(int *x, int *y) {
        int temp = *x;
        *x = *y;
        *y = temp;
    }
    
    int main() {
        int a = 1, b = 2;
        // 交换后a=2,b=1
        swap(&a, &b);
        return 0;
    }
    
  2. 动态内存分配(malloc/free):运行时按需分配内存(堆内存):

    c

    运行

    // 分配10个int的内存(堆区)
    int *arr = (int *)malloc(10 * sizeof(int));
    if (arr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    
    // 使用动态数组
    for (int i = 0; i < 10; i++) {
        arr[i] = i;
    }
    
    // 释放内存(避免内存泄漏)
    free(arr);
    // 置空,避免野指针
    arr = NULL;
    
  3. 实现复杂数据结构:链表、二叉树、栈、队列等均依赖指针实现节点间的关联。
  4. 提高程序效率:传递大对象(如结构体)时,传递指针仅需拷贝 8 字节(64 位),而非整个对象,减少内存拷贝开销。
六、指针的常见误区
  1. 混淆 * 的两种含义
    • 定义时:int *p 表示 p 是指针变量;
    • 使用时:*p 表示解引用,访问指向的数据。
  2. 数组名与指针的区别:数组名是常量地址(不可修改,如 arr++ 报错),指针变量是变量(可修改,如 p++ 合法)。
  3. 越界访问:指针访问超出数组 / 内存块的范围,导致内存破坏(如 arr[10] 当数组长度为 5 时)。
  4. 内存泄漏:动态分配的内存未释放(malloc 后未 free),导致系统资源耗尽。
总结

指针是一把 “双刃剑”:

  • 优势:灵活操作内存、提升程序效率、实现复杂逻辑;
  • 风险:易出现野指针、内存越界、内存泄漏等问题,需严格遵循规范。

核心口诀:

  • & 取地址,* 解引用;
  • 指针初始化,用完置 NULL;
  • 动态内存要释放,越界访问不可取。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值