C语言高频错误实例对比:8段代码帮你避开90%的坑

简介: 本文精选8组典型C语言错误与正确代码对比,直击数组越界、字符串溢出、野指针、内存泄漏、有无符号混用、返回局部地址、sizeof误用、未定义行为等高频陷阱,以实例培养安全编码直觉。(239字)

很多C语言的bug,不是因为算法复杂,而是因为忽略了最基础的语法细节和底层规则。本文用8组错误代码 vs 正确代码的直观对比,直击高频踩坑现场,用实例帮你建立安全编码的直觉。


1. 数组越界:差一个索引,毁一片内存

错误代码

#include <stdio.h>
int main() {
   
    int arr[5] = {
   1,2,3,4,5};
    // 致命错误:数组下标从0开始,最大为4
    for (int i = 0; i <= 5; i++) {
   
        printf("%d ", arr[i]); // arr[5]越界,破坏栈上其他数据
    }
    return 0;
}

坑点分析
C语言不做任何数组边界检查。arr[5]访问的是数组后方的未知栈内存,轻则打印乱码,重则修改函数返回地址,导致程序直接崩溃。

正确代码

#include <stdio.h>
int main() {
   
    int arr[5] = {
   1,2,3,4,5};
    // 安全边界:i < 数组长度
    for (int i = 0; i < 5; i++) {
   
        printf("%d ", arr[i]);
    }
    return 0;
}

2. 字符串溢出:看不见的\0才是杀手

错误代码

#include <stdio.h>
#include <string.h>
int main() {
   
    char str[5];
    strcpy(str, "hello"); // 致命错误:"hello"含\0共需6字节
    printf("%s\n", str);
    return 0;
}

坑点分析
C语言字符串以\0(ASCII码0)结尾。"hello"看似5个字符,实际占用6字节内存。溢出的\0会覆盖相邻内存,导致缓冲区溢出漏洞。

正确代码

#include <stdio.h>
#include <string.h>
int main() {
   
    char str[6]; // 预留\0的位置
    strcpy(str, "hello");
    // 更安全的写法:指定最大拷贝长度
    // strncpy(str, "hello", sizeof(str)-1);
    // str[sizeof(str)-1] = '\0'; // 手动确保结束
    printf("%s\n", str);
    return 0;
}

3. 野指针与空指针:未判空,不使用

错误代码

#include <stdio.h>
void print_val(int* p) {
   
    // 致命错误:未判空直接解引用
    printf("%d\n", *p);
}

int main() {
   
    int* p = NULL;
    print_val(p); // 传入空指针
    return 0;
}

坑点分析
NULL解引用,几乎必然触发段错误(Segmentation Fault),导致程序直接崩溃。任何指针在使用前,必须先判空。

正确代码

#include <stdio.h>
void print_val(int* p) {
   
    if (p != NULL) {
    // 先判空,后使用
        printf("%d\n", *p);
    } else {
   
        printf("空指针,无法访问\n");
    }
}

int main() {
   
    int val = 10;
    int* p = &val;
    print_val(p);
    return 0;
}

4. 内存泄漏与重复释放:成对管理,释放即置空

错误代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct User {
   
    char* name;
};

int main() {
   
    struct User* u = malloc(sizeof(struct User));
    u->name = malloc(10);
    strcpy(u->name, "test");

    free(u); // 致命错误1:只释放了结构体,没释放name指向的内存(内存泄漏)
    free(u); // 致命错误2:重复释放同一块内存(未定义行为)
    return 0;
}

坑点分析

  1. 只释放结构体外壳,内部指针指向的堆内存会永远丢失,造成内存泄漏;
  2. 重复释放同一块内存,会直接破坏堆内存管理结构,导致程序崩溃。

正确代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct User {
   
    char* name;
};

int main() {
   
    struct User* u = malloc(sizeof(struct User));
    if (u == NULL) return -1;

    u->name = malloc(10);
    if (u->name == NULL) {
   
        free(u); // 分配失败,记得释放已分配的内存
        u = NULL;
        return -1;
    }
    strcpy(u->name, "test");

    // 释放顺序:先内后外
    free(u->name);
    u->name = NULL; // 释放后立即置空
    free(u);
    u = NULL; // 释放后立即置空
    return 0;
}

5. 有符号与无符号比较:负数变极大值的陷阱

错误代码

#include <stdio.h>
#include <string.h>
int main() {
   
    char* s = "";
    // 致命错误:strlen返回size_t(无符号),0-1变成极大值,死循环
    for (int i = 0; i < strlen(s) - 1; i++) {
   
        printf("这行永远不会执行,但循环永远不会停\n");
    }
    return 0;
}

坑点分析
s为空字符串时,strlen(s) = 00 - 1在无符号运算中会变成0xFFFFFFFF(极大值),循环条件永远成立,导致死循环。

正确代码

#include <stdio.h>
#include <string.h>
int main() {
   
    char* s = "";
    size_t len = strlen(s);
    // 先判断长度,避免减法溢出;或显式转为有符号
    if (len > 1) {
   
        for (int i = 0; i < (int)len - 1; i++) {
   
            printf("安全执行\n");
        }
    }
    return 0;
}

6. 返回局部变量地址:栈帧销毁,地址失效

错误代码

#include <stdio.h>
int* get_val() {
   
    int a = 10;
    return &a; // 致命错误:返回局部变量地址
}

int main() {
   
    int* p = get_val();
    printf("%d\n", *p); // 解引用已失效的栈地址,结果不可控
    return 0;
}

坑点分析
局部变量a存储在栈上,get_val函数返回后,栈帧立即被销毁,&a指向的内存会被后续函数调用覆盖,解引用会读到垃圾值,甚至导致崩溃。

正确代码

#include <stdio.h>
#include <stdlib.h>

// 方案1:返回堆内存地址(调用者需负责free)
int* get_val_heap() {
   
    int* p = malloc(sizeof(int));
    if (p != NULL) {
   
        *p = 10;
    }
    return p;
}

// 方案2:使用static(线程不安全,仅适用于单线程)
int* get_val_static() {
   
    static int a = 10; // 静态变量存储在静态数据区,生命周期全程有效
    return &a;
}

int main() {
   
    int* p1 = get_val_heap();
    if (p1 != NULL) {
   
        printf("%d\n", *p1);
        free(p1);
        p1 = NULL;
    }
    return 0;
}

7. sizeof的单位陷阱:分配内存,勿忘乘类型大小

错误代码

#include <stdio.h>
#include <stdlib.h>
int main() {
   
    int* arr = malloc(5); // 致命错误:只分配了5字节,而非5个int
    for (int i = 0; i < 5; i++) {
   
        arr[i] = i; // 越界访问,触发未定义行为
    }
    free(arr);
    return 0;
}

坑点分析
malloc的参数是字节数。32位系统下int占4字节,存储5个int需要20字节,只分配5字节会导致严重的越界。

正确代码

#include <stdio.h>
#include <stdlib.h>
int main() {
   
    int* arr = malloc(5 * sizeof(int)); // 正确:元素个数 * 单个元素大小
    // 更健壮的写法:不依赖具体类型名
    // int* arr = malloc(5 * sizeof(*arr));
    if (arr == NULL) return -1;

    for (int i = 0; i < 5; i++) {
   
        arr[i] = i;
        printf("%d ", arr[i]);
    }
    free(arr);
    arr = NULL;
    return 0;
}

8. 序列点与未定义行为:一个表达式,变量只改一次

错误代码

#include <stdio.h>
int main() {
   
    int i = 0;
    // 致命错误:两个序列点之间,i被修改多次,结果完全不可控
    printf("%d %d\n", i++, i++);
    i = i++ + ++i; // 同样是未定义行为
    return 0;
}

坑点分析
C标准只保证序列点(如分号、&&左操作数后)的执行顺序。两个序列点之间,同一个变量被修改多次,编译器可以任意安排执行顺序,结果完全不可预测。

正确代码

#include <stdio.h>
int main() {
   
    int i = 0;
    // 拆分成独立语句,用临时变量保存结果,时序完全可控
    int t1 = i++;
    int t2 = i++;
    printf("%d %d\n", t1, t2); // 输出0 1

    i = 0;
    int a = i++;
    int b = ++i;
    i = a + b;
    printf("i = %d\n", i); // 输出2
    return 0;
}

总结

这8个实例覆盖了C语言开发中90%的高频低级错误。核心避坑原则可以总结为5句话:

  1. 数组下标不越界,字符串预留\0
  2. 指针未判空,绝不解引用
  3. malloc与free成对出现,释放后立即置空
  4. 有符号无符号不混用,返回值不碰局部栈
  5. 一个表达式,同一个变量最多修改一次

时刻牢记这5条原则,你就能避开绝大多数C语言的底层陷阱,写出更稳定、更安全的代码。

相关文章
|
10天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
5495 13
|
18天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
21829 117
|
14天前
|
人工智能 安全 前端开发
Team 版 OpenClaw:HiClaw 开源,5 分钟完成本地安装
HiClaw 基于 OpenClaw、Higress AI Gateway、Element IM 客户端+Tuwunel IM 服务器(均基于 Matrix 实时通信协议)、MinIO 共享文件系统打造。
8304 8

热门文章

最新文章