文章目录
-
- 一、课程导入
- 二、核心知识点讲解
-
- (一)for循环边界错误
-
- [1. 错误根源](#1. 错误根源)
- [2. 典型错误场景](#2. 典型错误场景)
- [3. 错误示例](#3. 错误示例)
- [4. 修正方法](#4. 修正方法)
- [5. 修正示例](#5. 修正示例)
- (二)逻辑运算符混淆及优先级问题
-
- [1. 错误根源](#1. 错误根源)
- [2. 典型错误场景](#2. 典型错误场景)
- [3. 错误示例](#3. 错误示例)
- [4. 修正方法](#4. 修正方法)
- [5. 修正示例](#5. 修正示例)
- (三)未初始化局部变量的危害
-
- [1. 错误根源](#1. 错误根源)
- [2. 典型错误场景](#2. 典型错误场景)
- [3. 错误示例](#3. 错误示例)
- [4. 修正方法](#4. 修正方法)
- [5. 修正示例](#5. 修正示例)
- 三、课堂实操练习
- 四、课后作业
-
- [作业1 基础题](#作业1 基础题)
- [作业2 提升题](#作业2 提升题)
- 五、课程总结
- 六、核心关键词
- [上一节课第4课答案 字符串与sizeof使用误区 实战作业代码](#上一节课第4课答案 字符串与sizeof使用误区 实战作业代码)
课程定位:逻辑与流程控制类核心课程,聚焦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整除的数",要求:
-
使用for循环实现,避免循环边界错误;
-
条件判断使用正确的逻辑运算符,加括号明确优先级;
-
所有局部变量声明后立即初始化;
-
最终输出结果为: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;
}
五、课程总结
-
循环与条件判断错误属于逻辑错误 ,无编译报错,需通过边界值测试、逻辑梳理排查;
-
for循环的核心是把控初始值、终止条件、步长 ,数组遍历终止条件优先使用
循环变量 < 数组长度; -
避免逻辑运算符混淆,多条件判断时强制加括号,明确运算顺序,减少优先级带来的错误;
-
局部变量无自动初始化特性,声明即初始化 是规避此类错误的核心习惯,配合
-Wall编译警告可提前发现问题; -
编写完流程控制代码后,代入正常值、边界值、异常值测试,验证逻辑是否符合预期。
六、核心关键词
循环边界、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核心误区设计,对比字符数组 与字符串指针 、sizeof与strlen的使用差异,验证无\0终止符的字符数组对strlen的影响。代码先定义不同类型的字符数组和指针,输出其sizeof计算结果,体现sizeof计算内存占用、与字符串实际长度无关的特性;再通过strlen计算有效字符串长度,演示手动添加\0的必要性。同时实现安全的strcpy字符串拷贝,编写函数验证数组作为函数参数退化为指针 ,sizeof无法获取原数组长度,需手动传递数组长度,全面覆盖本课的高频易错点。
三、作业注意事项
-
sizeof的返回值类型为size_t,打印时需使用格式符%zu,避免格式不匹配导致的输出错误; -
strlen仅计算\0之前的有效字符数,无\0的字符数组使用strlen会导致内存越界读取,必须手动添加终止符; -
字符串拷贝时,目标字符数组的长度必须大于等于源字符串长度+1 (预留
\0位置),避免strcpy越界; -
数组作为函数参数时,会自动退化为指向首元素的指针,函数内使用
sizeof仅能获取指针长度,需通过参数传递原数组实际长度; -
区分字符数组初始化方式:
char str[] = "abc"会自动添加\0,char str[] = {'a','b','c'}则不会,需根据需求手动添加; -
编译代码时开启
-Wall警告,及时发现字符串操作中的潜在越界、类型不匹配问题。
上一节课链接 : C语言逆向学习基础课 第 4 课:字符串与 sizeof 的使用误区详解及实战修正
下一节课链接 : C语言逆向学习基础课 第 6 课:switch与goto语句的正确使用