C语言逆向学习基础课 第 5 课:循环与条件判断陷阱深度解析

文章目录

课程定位:逻辑与流程控制类核心课程,聚焦C语言循环与条件判断中的高频逻辑陷阱,从错误根源出发讲解规避方法与修正技巧

一、课程导入

循环与条件判断是C语言流程控制的核心,也是实战中逻辑错误的高发区,这类错误无编译报错提示,仅表现为运行结果异常或程序逻辑混乱,排查难度大。

本课将拆解for循环边界、逻辑运算符混淆、局部变量未初始化三类核心陷阱,结合典型案例分析根源,通过实操掌握修正方法,从根本上规避此类逻辑错误。

二、核心知识点讲解

(一)for循环边界错误

1. 错误根源

C语言for循环的终止条件设置不当循环变量自增/自减时机错误,或忽略"数组下标从0开始"的特性,导致循环多执行、少执行或死循环。

2. 典型错误场景
  • 终止条件使用>=/<=代替</>,适配数组长度时越界;

  • 循环体内提前修改循环变量,导致循环逻辑混乱;

  • 死循环:循环变量未做自增/自减操作,或终止条件恒为真。

3. 错误示例
C 复制代码
#include <stdio.h>
int main() {
    int arr[5] = {1,2,3,4,5};
    // 错误:终止条件i<=5,数组下标最大为4,导致越界访问
    for(int i=0; i<=5; i++){
        printf("%d ", arr[i]);
    }
    return 0;
}
4. 修正方法
  • 数组遍历的终止条件统一使用循环变量 < 数组长度

  • 循环体内尽量避免修改循环变量,如需调整需明确逻辑;

  • 编写for循环时,先确定初始值、终止条件、步长三个核心要素。

5. 修正示例
C 复制代码
#include <stdio.h>
int main() {
    int arr[5] = {1,2,3,4,5};
    // 正确:终止条件i<5,匹配数组实际长度
    for(int i=0; i<5; i++){
        printf("%d ", arr[i]);
    }
    return 0;
}

(二)逻辑运算符混淆及优先级问题

1. 错误根源
  • 混淆逻辑与&&(全真才真)和逻辑或||(一真即真)的语义;

  • 忽略逻辑运算符的优先级:&&优先级高于||,未加括号时导致判断逻辑偏离预期。

2. 典型错误场景
  • 需求为"同时满足两个条件"却使用||,需求为"满足其一即可"却使用&&

  • 多条件判断时,未为低优先级逻辑运算加括号,导致运算顺序错误。

3. 错误示例
C 复制代码
#include <stdio.h>
int main() {
    int a = 3, b = 5;
    // 错误:需求是a>2 且 b<10,却误用||,逻辑判断失真
    if(a>2 || b<10){
        printf("条件成立\n");
    }else{
        printf("条件不成立\n");
    }
    // 错误:未加括号,&&优先级高,实际执行(a>2&&b>4)||b<10,偏离原需求
    if(a>2&&b>4||b<10){
        printf("多条件判断成立\n");
    }
    return 0;
}
4. 修正方法
  • 明确业务需求,严格区分&&||的语义,可在注释中注明判断逻辑;

  • 多条件判断时,无论优先级高低,均为不同逻辑块加括号,提升代码可读性与正确性;

  • 编写完条件判断后,代入边界值测试逻辑是否符合预期。

5. 修正示例
C 复制代码
#include <stdio.h>
int main() {
    int a = 3, b = 5;
    // 正确:使用&&匹配"同时满足"的需求
    if(a>2 && b<10){
        printf("条件成立\n");
    }else{
        printf("条件不成立\n");
    }
    // 正确:加括号明确运算顺序,匹配原需求
    if(a>2 && (b>4||b<10)){
        printf("多条件判断成立\n");
    }
    return 0;
}

(三)未初始化局部变量的危害

1. 错误根源

C语言中局部变量(栈上变量)不会被编译器自动初始化为0,若声明后未赋值直接使用,会读取内存中的随机垃圾值,导致条件判断、循环计算结果异常。

2. 典型错误场景
  • 声明局部变量后,直接用于条件判断的表达式中;

  • 局部变量作为循环计数器,未初始化直接开始循环。

3. 错误示例
C 复制代码
#include <stdio.h>
int main() {
    int num; // 错误:局部变量未初始化
    // 读取随机值,判断结果不可控
    if(num > 0){
        printf("num为正数\n");
    }else{
        printf("num为非正数\n");
    }
    return 0;
}
4. 修正方法
  • 声明局部变量后立即初始化,默认值根据业务需求设为0、-1等;

  • 编译时开启-Wall警告,编译器会提示"未初始化的局部变量使用"问题;

  • 养成编码习惯:变量声明与初始化写在同一行。

5. 修正示例
C 复制代码
#include <stdio.h>
int main() {
    int num = 0; // 正确:声明后立即初始化
    if(num > 0){
        printf("num为正数\n");
    }else{
        printf("num为非正数\n");
    }
    return 0;
}

三、课堂实操练习

练习要求

找出以下代码中的3处循环与条件判断陷阱,分析错误根源并修正,要求代码能正确输出1 3 5 7 9,无越界、无逻辑错误。

错误代码

C 复制代码
#include <stdio.h>
int main() {
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i, flag;
    for(i=0; i<=10; i++){
        if(flag && arr[i]%2==1){
            printf("%d ", arr[i]);
        }
    }
    return 0;
}

参考修正答案

C 复制代码
#include <stdio.h>
int main() {
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0, flag = 1; // 修正1:局部变量i、flag立即初始化
    for(i=0; i<10; i++){ // 修正2:终止条件i<10,避免数组越界
        if(flag && arr[i]%2==1){ // 修正3:flag初始化为1,条件判断有效
            printf("%d ", arr[i]);
        }
    }
    return 0;
}

实操要点

  • 编译运行错误代码,观察运行结果;

  • 结合本课知识点逐一排查陷阱,修改后重新编译;

  • 验证输出结果是否符合预期,理解每一处修正的原因。

四、课后作业

作业1 基础题

编写一个C语言程序,实现"遍历1-100的整数,输出其中能同时被3和5整除的数",要求:

  1. 使用for循环实现,避免循环边界错误;

  2. 条件判断使用正确的逻辑运算符,加括号明确优先级;

  3. 所有局部变量声明后立即初始化;

  4. 最终输出结果为:15 30 45 60 75 90。

作业2 提升题

找出以下代码中的循环与条件判断错误,分析错误类型并修正,说明代码的原需求与错误导致的问题,修正后保证程序能正常运行并输出正确结果。

C 复制代码
#include <stdio.h>
int main() {
    int score[6] = {85, 92, 78, 65, 90, 88};
    int count;
    for(int j=0; j>=6; j++){
        if(score[j] >= 80 || score[j] < 90){
            count++;
        }
    }
    printf("80-89分的学生人数:%d\n", count);
    return 0;
}

五、课程总结

  1. 循环与条件判断错误属于逻辑错误 ,无编译报错,需通过边界值测试、逻辑梳理排查;

  2. for循环的核心是把控初始值、终止条件、步长 ,数组遍历终止条件优先使用循环变量 < 数组长度

  3. 避免逻辑运算符混淆,多条件判断时强制加括号,明确运算顺序,减少优先级带来的错误;

  4. 局部变量无自动初始化特性,声明即初始化 是规避此类错误的核心习惯,配合-Wall编译警告可提前发现问题;

  5. 编写完流程控制代码后,代入正常值、边界值、异常值测试,验证逻辑是否符合预期。

六、核心关键词

循环边界、for循环、逻辑与&&、逻辑或||、运算符优先级、局部变量、未初始化、逻辑错误、数组遍历


上一节课第4课答案 字符串与sizeof使用误区 实战作业代码

一、实战作业代码

C 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    // 定义字符数组与字符串指针,对比sizeof与strlen的差异
    char str1[20] = "C Language";
    char str2[] = "Error";
    char *pstr = str1;
    char str3[10] = {'a', 'b', 'c'}; // 无\0终止符的字符数组

    // 输出各类sizeof计算结果
    printf("str1数组sizeof:%zu\n", sizeof(str1));
    printf("str2数组sizeof:%zu\n", sizeof(str2));
    printf("指针pstrsizeof:%zu\n", sizeof(pstr));
    printf("str3数组sizeof:%zu\n", sizeof(str3));

    // 输出各类strlen计算结果,注意str3无\0的问题
    printf("str1字符串strlen:%zu\n", strlen(str1));
    printf("str2字符串strlen:%zu\n", strlen(str2));
    printf("指针pstrstrlen:%zu\n", strlen(pstr));
    // 为str3手动添加\0后计算strlen
    str3[3] = '\0';
    printf("str3添加\\0后strlen:%zu\n", strlen(str3));

    // 字符串拷贝实操,规避strcpy越界风险
    char dest[20] = {0};
    strcpy(dest, str1); // 目标数组长度足够,安全拷贝
    printf("拷贝后dest:%s\n", dest);

    // 演示sizeof作为数组长度参数的正确用法
    void printArr(char arr[], int len);
    printArr(str2, sizeof(str2)/sizeof(str2[0]));

    return 0;
}

// 函数参数中数组退化为指针,sizeof无法获取原数组长度
void printArr(char arr[], int len) {
    printf("函数内arr的sizeof:%zu\n", sizeof(arr));
    printf("字符数组内容:");
    for(int i=0; i<len; i++){
        printf("%c ", arr[i]);
    }
    printf("\n");
}

二、代码功能说明

本代码围绕字符串与sizeof核心误区设计,对比字符数组字符串指针sizeofstrlen的使用差异,验证无\0终止符的字符数组对strlen的影响。代码先定义不同类型的字符数组和指针,输出其sizeof计算结果,体现sizeof计算内存占用、与字符串实际长度无关的特性;再通过strlen计算有效字符串长度,演示手动添加\0的必要性。同时实现安全的strcpy字符串拷贝,编写函数验证数组作为函数参数退化为指针sizeof无法获取原数组长度,需手动传递数组长度,全面覆盖本课的高频易错点。

三、作业注意事项

  1. sizeof的返回值类型为size_t,打印时需使用格式符%zu,避免格式不匹配导致的输出错误;

  2. strlen仅计算\0之前的有效字符数,无\0的字符数组使用strlen会导致内存越界读取,必须手动添加终止符;

  3. 字符串拷贝时,目标字符数组的长度必须大于等于源字符串长度+1 (预留\0位置),避免strcpy越界;

  4. 数组作为函数参数时,会自动退化为指向首元素的指针,函数内使用sizeof仅能获取指针长度,需通过参数传递原数组实际长度;

  5. 区分字符数组初始化方式:char str[] = "abc"会自动添加\0char str[] = {'a','b','c'}则不会,需根据需求手动添加;

  6. 编译代码时开启-Wall警告,及时发现字符串操作中的潜在越界、类型不匹配问题。

上一节课链接 : C语言逆向学习基础课 第 4 课:字符串与 sizeof 的使用误区详解及实战修正

下一节课链接 : C语言逆向学习基础课 第 6 课:switch与goto语句的正确使用

相关推荐
RFCEO2 天前
C语言逆向学习基础课 第2课:内存分配与释放的深度误区
c语言逆向学习基础课·内存分配与释放·分配后不校验是否成功·计算大小错误·realloc直接覆盖原指针·重复释放·释放后不置空
RFCEO2 天前
C语言逆向学习基础课 第7课 函数参数传递与返回值陷阱
c语言逆向学习基础课·c语言函数参数传递陷阱·c语言值传递与地址传递区别·c语言返回局部变量指针错误·c语言函数返回值校验·c语言堆内存分配与释放
m0_4886333217 天前
C语言变量命名规则、入门自学、运算符优先级及数据结构介绍
c语言·数据结构·运算符优先级·变量命名·入门自学
RFCEO1 个月前
JavaScript基础课程五、JavaScript 运算符与流程控制(条件判断)
运算符优先级·零基础javascript·算术比较逻辑赋值·多条件判断用法·case语句break穿透·流程控制语句使用
CS创新实验室10 个月前
CPython 局部变量存储机制:从 “栈上存储“ 看语言实现差异
python···cpython·局部变量
Thanks_ks1 年前
19 C 语言位运算、赋值、条件、逗号运算符详解:涵盖运算符优先级与复杂表达式计算过程分析
位运算·运算符优先级·赋值运算符·三元运算符·c 语言运算符·逗号运算符·复杂表达式解析
柏箱2 年前
Java作用域
java·作用域·全局变量·局部变量
martian6652 年前
学懂C语言(十六):对C语言作用域规则 局部变量、全局变量的认识
c语言·开发语言·作用域·全局变量·局部变量
oscar9992 年前
活用变量,让Postman的使用飞起来
测试工具·postman·变量·全局变量·局部变量·集合变量