← C 语言 学习路线(共 8 章)

第 6 章 指针与动态内存

red wenzi · 2026-09-15 · 编程语言 · C 语言 · 📖 预计阅读 12 分钟 · 共 2 道练习
🎯 本章你会学到:指针的本质、指针与数组的关系、malloc/free 的正确用法与常见错误。建议边读边敲,每节练习先自己做,再展开答案对照。

指针是 C 的核心,也是最容易出错的地方。一句话理解:指针是一个存地址的变量,*p 就是访问那个地址上的东西。

先看一个必须用指针才能完成的任务

用指针交换两个变量
#include <stdio.h>

static void swap(int *a, int *b) {
    int t = *a;      /* 取出 a 指向的值 */
    *a = *b;         /* 把 b 指向的值写进 a 指向的位置 */
    *b = t;
}

int main(void) {
    int x = 1, y = 2;
    swap(&x, &y);    /* 传地址,函数里才能改到 x、y */
    printf("x=%d y=%d\n", x, y);
    return 0;
}
运行结果
x=2 y=1

输出 x=2 y=1。因为 C 是值传递,swap(x, y) 只会交换副本;只有传地址 &x,函数才能改到调用方的变量。

动态内存:malloc 与 free

动态数组
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n = 0;
    if (scanf("%d", &n) != 1 || n <= 0) {
        return 1;
    }

    int *a = malloc((size_t)n * sizeof(int));   /* 申请 n 个 int */
    if (a == NULL) {                            /* 一定要检查是否成功 */
        return 1;
    }

    for (int i = 0; i < n; ++i) {
        a[i] = i * i;                           /* a[i] 等价于 *(a + i) */
    }

    long long sum = 0;
    for (int i = 0; i < n; ++i) {
        sum += a[i];
    }
    printf("sum=%lld\n", sum);

    free(a);        /* 用完必须释放,且只能释放一次 */
    a = NULL;       /* 置空,避免变成野指针 */
    return 0;
}
输入
4
运行结果
sum=14

输入 4 输出 sum=14(0+1+4+9)。malloc 只管要空间、不管清零;calloc 会清零,realloc 用来扩容。

⚠️ 易错点 忘记 free 会内存泄漏;忘记检查 malloc 返回值会崩溃。这两个是 C 语言作业里最常被扣分的点。
⚠️ 易错点 返回局部变量的地址是错的:函数一返回,那块栈内存就失效了,调用方拿到的是悬垂指针。

✍️ 本节练习

6.1必做用指针输出最值

写函数 void minmax(const int *a, int n, int *out_min, int *out_max),把最小值和最大值写到两个输出参数里。

输入 第一行 n;第二行 n 个整数。

输出 两行:min=最小值max=最大值

样例输入
6
5 3 9 1 7 4
样例输出
min=1
max=9

💡 提示 要写回调用方的变量,参数就得是指针;const int *a 表示只读数组。

✅ 查看参考答案与解析
#include <stdio.h>

#define MAXN 1000

static void minmax(const int *a, int n, int *out_min, int *out_max) {
    int lo = a[0], hi = a[0];
    for (int i = 1; i < n; ++i) {
        if (a[i] < lo) { lo = a[i]; }
        if (a[i] > hi) { hi = a[i]; }
    }
    *out_min = lo;    /* 通过指针写回调用方 */
    *out_max = hi;
}

int main(void) {
    int n = 0;
    if (scanf("%d", &n) != 1 || n <= 0 || n > MAXN) {
        return 1;
    }
    int a[MAXN];
    for (int i = 0; i < n; ++i) {
        if (scanf("%d", &a[i]) != 1) {
            return 1;
        }
    }
    int lo = 0, hi = 0;
    minmax(a, n, &lo, &hi);
    printf("min=%d\n", lo);
    printf("max=%d\n", hi);
    return 0;
}

解析 这就是 C 里“返回多个值”的标准做法:入参用 const 指针(只读),出参用普通指针(可写)。

6.2挑战动态数组原地反转

malloc 申请数组读入 n 个数,用两个指针就地反转,再输出。要求释放内存。

输入 第一行 n;第二行 n 个整数。

输出 一行:反转后的序列。

样例输入
5
1 2 3 4 5
样例输出
5 4 3 2 1

💡 提示 左右两个指针向中间靠拢,交换 *left*right,直到相遇。

✅ 查看参考答案与解析
#include <stdio.h>
#include <stdlib.h>

static void reverse(int *first, int *last) {   /* [first, last) 左闭右开 */
    if (first == last) {
        return;
    }
    --last;
    while (first < last) {
        int t = *first;
        *first = *last;
        *last = t;
        ++first;
        --last;
    }
}

int main(void) {
    int n = 0;
    if (scanf("%d", &n) != 1 || n <= 0) {
        return 1;
    }
    int *a = malloc((size_t)n * sizeof(int));
    if (a == NULL) {
        return 1;
    }
    for (int i = 0; i < n; ++i) {
        if (scanf("%d", &a[i]) != 1) {
            free(a);
            return 1;
        }
    }

    reverse(a, a + n);

    for (int i = 0; i < n; ++i) {
        printf("%d%c", a[i], i + 1 == n ? '\n' : ' ');
    }
    free(a);
    return 0;
}

解析 区间用左闭右开 [first, last) 是 C/C++ 的通用约定(STL 也是这个约定);出错时要先 free 再返回,避免泄漏。

📚 本文概念都在知识大全:

C 语言 · GCC 编译器 · 指针 · 动态内存 malloc/free · C 字符串 · 结构体 · 预处理 · 编译与链接