C语言指针基础 #

一、指针概述 #

1.1 什么是指针 #

指针是一个变量,其值为另一个变量的地址。

c
int a = 10;
int* p = &a;
  • a 是一个整数变量,值为10
  • &a 是变量a的地址
  • p 是一个指针,存储a的地址

1.2 内存与地址 #

text
内存地址        值
┌─────────┬─────────┐
│ 0x1000  │    10   │ ← 变量 a
├─────────┼─────────┤
│ 0x1004  │ 0x1000  │ ← 指针 p
└─────────┴─────────┘

1.3 为什么需要指针 #

  • 直接访问内存
  • 函数间传递大数据
  • 动态内存分配
  • 实现数据结构

二、指针声明与初始化 #

2.1 声明指针 #

c
int* pi;
float* pf;
char* pc;
double* pd;
void* pv;

语法:类型* 指针名;

2.2 初始化指针 #

c
int a = 10;
int* p = &a;

& 是取地址运算符,返回变量的地址。

2.3 空指针 #

c
int* p = NULL;
int* q = 0;

NULL 是空指针常量,表示指针不指向任何有效地址。

c
#include <stdio.h>
#include <stddef.h>

int main() {
    int* p = NULL;
    
    if (p == NULL) {
        printf("指针为空\n");
    }
    return 0;
}

2.4 未初始化指针 #

c
int* p;
printf("%p\n", p);
*p = 10;

未初始化的指针包含随机地址,使用会导致未定义行为。

三、指针使用 #

3.1 取地址运算符 & #

c
int a = 10;
printf("a的值: %d\n", a);
printf("a的地址: %p\n", &a);

输出:

text
a的值: 10
a的地址: 0x7fff5fbff8ac

3.2 解引用运算符 * #

c
int a = 10;
int* p = &a;

printf("p的值: %p\n", p);
printf("p指向的值: %d\n", *p);

* 是解引用运算符,获取指针指向的值。

3.3 通过指针修改值 #

c
#include <stdio.h>

int main() {
    int a = 10;
    int* p = &a;
    
    printf("修改前: a = %d\n", a);
    *p = 20;
    printf("修改后: a = %d\n", a);
    return 0;
}

输出:

text
修改前: a = 10
修改后: a = 20

3.4 指针大小 #

c
#include <stdio.h>

int main() {
    printf("char*: %zu字节\n", sizeof(char*));
    printf("int*: %zu字节\n", sizeof(int*));
    printf("double*: %zu字节\n", sizeof(double*));
    printf("void*: %zu字节\n", sizeof(void*));
    return 0;
}

输出(64位系统):

text
char*: 8字节
int*: 8字节
double*: 8字节
void*: 8字节

所有指针大小相同,因为它们都存储地址。

四、指针类型 #

4.1 不同类型指针 #

c
int a = 10;
float b = 3.14;
char c = 'A';

int* pi = &a;
float* pf = &b;
char* pc = &c;

4.2 void指针 #

void指针可以指向任何类型:

c
#include <stdio.h>

int main() {
    int a = 10;
    void* p = &a;
    
    int* pi = (int*)p;
    printf("%d\n", *pi);
    return 0;
}

void指针使用前需要类型转换。

4.3 指针类型的作用 #

c
#include <stdio.h>

int main() {
    int a = 0x12345678;
    char* p = (char*)&a;
    
    printf("int值: 0x%x\n", a);
    printf("第一个字节: 0x%x\n", *p);
    return 0;
}

指针类型决定了解引用时读取的字节数。

五、指针运算 #

5.1 指针加减整数 #

c
#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p = arr;
    
    printf("p[0] = %d\n", *p);
    printf("p[1] = %d\n", *(p + 1));
    printf("p[2] = %d\n", *(p + 2));
    return 0;
}

p + 1 实际偏移 sizeof(int) 字节。

5.2 指针减指针 #

c
#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p1 = &arr[0];
    int* p2 = &arr[4];
    
    printf("元素个数: %ld\n", p2 - p1);
    return 0;
}

输出:

text
元素个数: 4

5.3 指针比较 #

c
#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p = arr;
    
    while (p < arr + 5) {
        printf("%d ", *p);
        p++;
    }
    printf("\n");
    return 0;
}

六、指针与数组 #

6.1 数组名与指针 #

c
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;

数组名是首元素的地址。

6.2 下标访问与指针访问 #

c
#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p = arr;
    
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d, *(p+%d) = %d\n", 
               i, arr[i], i, *(p + i));
    }
    return 0;
}

arr[i] 等价于 *(arr + i)

6.3 遍历数组 #

c
#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p = arr;
    
    for (int i = 0; i < 5; i++) {
        printf("%d ", *p++);
    }
    printf("\n");
    return 0;
}

七、指针与函数 #

7.1 指针作为参数 #

c
#include <stdio.h>

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    printf("交换前: x=%d, y=%d\n", x, y);
    swap(&x, &y);
    printf("交换后: x=%d, y=%d\n", x, y);
    return 0;
}

输出:

text
交换前: x=10, y=20
交换后: x=20, y=10

7.2 返回指针 #

c
#include <stdio.h>

int* find_max(int* arr, int size) {
    int* max = arr;
    for (int i = 1; i < size; i++) {
        if (arr[i] > *max) {
            max = &arr[i];
        }
    }
    return max;
}

int main() {
    int arr[] = {3, 1, 4, 1, 5, 9, 2, 6};
    int* max = find_max(arr, 8);
    printf("最大值: %d\n", *max);
    return 0;
}

注意:不要返回局部变量的地址。

八、常量指针 #

8.1 指向常量的指针 #

c
int a = 10;
const int* p = &a;

*p = 20;

不能通过指针修改值,但可以改变指针指向。

8.2 常量指针 #

c
int a = 10;
int b = 20;
int* const p = &a;

p = &b;

指针本身是常量,不能改变指向。

8.3 指向常量的常量指针 #

c
int a = 10;
const int* const p = &a;

既不能修改指向的值,也不能改变指针指向。

8.4 总结 #

类型 能否修改指向 能否修改值
int* p
const int* p
int* const p
const int* const p

九、指针安全 #

9.1 空指针检查 #

c
void func(int* p) {
    if (p == NULL) {
        return;
    }
    *p = 10;
}

9.2 避免野指针 #

c
int* p = NULL;
p = malloc(sizeof(int) * 10);
if (p != NULL) {
    free(p);
    p = NULL;
}

9.3 指针初始化 #

c
int* p = NULL;
int* q = &a;

始终初始化指针,避免未定义行为。

十、总结 #

指针要点 #

概念 说明
声明 类型* 指针名;
初始化 int* p = &a;
取地址 &变量
解引用 *指针
空指针 NULL

指针运算 #

运算 说明
p + n 偏移 n * sizeof(类型)
p - n 向后偏移
p2 - p1 元素个数
p1 < p2 地址比较

常量指针 #

c
const int* p;
int* const p;
const int* const p;

安全建议 #

  • 始终初始化指针
  • 使用前检查NULL
  • free后置NULL
  • 避免返回局部变量地址

下一步,让我们学习指针运算!

最后更新:2026-03-26