国际访客建议访问 Primers 编程伙伴 国际版站点 > C 教程 > 指针 以获得更好的体验。

# C 语言的指针

指针是 C 语言中的一个重要概念,在类型名称后面添加一个星号(*)来表示对应的指针类型,例如 int* 表示指向 int 类型的指针。

指针类型的变量中保存的是内存地址,指针的类型表示如何解读该内存中的值。

示例:

#include <stdio.h>

int main(void)
{
    int* ptr = (int*)4;                         // 定义整型指针 ptr,它的初始值为 4
    printf("指针指向地址为 %p 的内存\n", ptr);  // 通过 %p 打印指针本身
    printf("该内存里的整型值为 %d\n", *ptr);    // *ptr 取值,因为 ptr 的类型是 int* 因此按照 int 类型进行取值

    *ptr = 8080;                                    // 将该内存地址中的值修改为 8080
    printf("该内存里的整型值被修改为 %d\n", *ptr);  // *ptr 取值,因为 ptr 的类型是 int* 因此按照 int 类型进行取值

    return 0;
}

说明:

  • printf 函数使用 %p 打印指针类型本身

  • *ptr 中的 *间接访存运算符,有时也称 解引用运算符,即访问内存中地址为 ptr 处的值

flowchart LR
    subgraph 内存
        m0["地址:0 <br/> 值:1000"]
        m1["地址:4 <br/> 值:2333"]
        m2["地址:8 <br/> 值:6666"]
    end
    
    ptr --> m1

运行结果:

指针指向地址为 0x2 的内存
该内存里的整型值为 2333
该内存里的整型值被修改为 8080

上述代码在现代操作系统上是无法运行的。因为现代操作系统使用保护模式,应用程序不能直接访问整个内存,只能间接访问部分内存。

示例:

#include <stdio.h>

int main(void)
{
    int x = 2333;                               // 定义整型变量 x
    int* ptr = &x;                              // 定义整型指针 ptr,它的初始值为变量 x 的地址

    printf("指针指向地址为 %p 的内存\n", ptr);  // 通过 %p 打印指针本身
    printf("该内存里的整型值为 %d\n", *ptr);    // *ptr 取值,因为 ptr 的类型是 int* 因此按照 int 类型进行取值

    *ptr = 8080;                                    // 将该内存地址中的值修改为 8080
    printf("该内存里的整型值被修改为 %d\n", *ptr);  // *ptr 取值,因为 ptr 的类型是 int* 因此按照 int 类型进行取值

    return 0;
}

说明:

  • printf 函数使用 %p 打印指针类型本身

  • &x 中的 &取地址运算符,有时也称 引用运算符,即获取变量在内存中的地址

  • *ptr 中的 *间接访存运算符,有时也称 解引用运算符,即访问内存中地址为 ptr 处的值

flowchart LR
    subgraph 内存
        m1["变量:x <br/> 值:2333"]
    end
    
    ptr --> m1

运行结果:

指针指向地址为 0x7ffe9e866f2c 的内存
该内存里的整型值为 2333
该内存里的整型值被修改为 8080

# NULL

C 语言中使用 NULL 表示无效的指针,它的值是 0。

int* ptr = NULL;    // 初始值设为 NULL

# void*

C 语言中使用 void* 表示泛型指针(任意类型的指针),提供灵活的内存操作和数据类型抽象,尤其在底层编程、内存管理和跨类型数据传递中非常关键。

它不能进行计算和解引用,必须转换成特定类型的指针才能解引用。

int x = 10;
void* ptr = (void*)&x;
int* ptr2 = (int*)ptr;

# 指针的运算

指针中保存的是内存地址,本质上是一个整数,因此指针可以进行计算。

示例:

#include <stdio.h>

int main(void)
{
    int* ptr = (int*)4;     // 定义整型指针 ptr,它的初始值是 4
    printf("ptr 指向地址为 %p 的内存\n", ptr);          // 打印 ptr 的地址

    ptr += 1;               // 将指针的值加一
    printf("ptr 指向地址为 %p 的内存\n", ptr);          // 打印 ptr 的地址

    return 0;
}

说明:

  • ptr += 1 将指针的地址加一

    • ptr 的值实际增加量不是 1 而是 4, 因为 ptrint 类型的指针,因此地址量增加为 int 类型的字节数

运行结果:

ptr 指向地址为 0x4 的内存
ptr 指向地址为 0x8 的内存

# 指针常量和常量指针

int* const ptr; // 指针常量,指向 int
const int* ptr; // 常量指针,指向 const int
  • 指针常量 是不能被修改的指针,即指针自身是常量不可修改。

  • 常量指针 是指向常量的指针,即不能通过指针修改被指向的变量。

示例:

int x = 10;             // 定义变量 x
int y = 30;             // 定义变量 y

int* const ptr = &x;    // 定义指针常量
*ptr = 20;              // 可以通过指针修改内存中的值
ptr = &y;               // 错误!指针常量 ptr 自身不能被修改

const int* ptr2 = &x;   // 定义常量指针
ptr2 = &y;              // 可以修改指针 ptr2 的值,将其指向 y
*ptr2 = 40;             // 错误!常量指针 ptr2 不能修改指向的内存

# 指向指针的指针

指针中保存的内存地址也可以是另一个指针,例如 int** 指向 int*

示例:

#include <stdio.h>

int main(void)
{
    int x = 2333;                               // 定义整型变量 x
    int* ptr = &x;                              // 定义整型指针 ptr,它的初始值为变量 x 的地址
    int** ptr_to_ptr = &ptr;                    // 定义整型指针的指针 ptr_ptr,它的初始值为变量 ptr 的地址

    printf("ptr_to_ptr 指向地址为 %p 的内存\n", ptr_to_ptr);    // 通过 %p 打印指针本身
    printf("ptr 指向地址为 %p 的内存\n", *ptr_to_ptr);          // *ptr_to_ptr 取值,因为 ptr_to_ptr 的类型是 int** 因此按照 int* 类型进行取值
    printf("x 的值为 %d\n", **ptr_to_ptr);                      // **ptr_to_ptr 取值,因为 *ptr_to_ptr 的类型是 int* 因此按照 int 类型进行取值

    return 0;
}

说明:

  • ptr 指向 x

  • ptr_to_ptr 指向 ptr

flowchart LR
    subgraph 内存
        m1["变量:x <br/> 值:2333"]
    end
    
    ptr --> m1

    ptr_to_ptr --> ptr

运行结果:

ptr_to_ptr 指向地址为 0x7ffcf0bbc558 的内存
ptr 指向地址为 0x7ffcf0bbc554 的内存
x 的值为 2333

Anya 的指针示意图.png

# 同时定义多个指针时的陷阱

不可用下面这段代码的方式定义多个指针:

int* x, y;
  • x 的类型是 int*

  • y 的类型是 int

这是 C 语言的设计缺陷,容易导致误解。

正确的写法是:

int *x, *y;
  • x 的类型是 int*

  • y 的类型是 int*

本文 更新于: 2025-11-27 09:38:05 创建于: 2025-11-27 09:38:05