c语言for循环中的易错点,易错点:for循环中循环变量的更新所带来的问题

本文探讨了C语言for循环中循环变量更新可能导致的问题,通过两个实例详细解释了如何处理这些问题。第一个实例涉及里程表跳过特定数字的计算,强调了循环变量更新时要考虑后续操作的影响。第二个实例展示了蛇形矩阵打印,分析了循环初始化和更新对矩阵填充的影响。这两个例子揭示了for循环中循环变量更新的微妙之处,提醒程序员注意此类潜在错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

易错点:for循环中循环变量的更新所带来的问题

标签:C语言 for循环 易错点

by 小威威

1.for循环的介绍

for循环的格式如下:

for (表达式1;表达式2;表达式3)

其中,表达式1指初始化表达式,表达式2指循环条件表达式,表达式3指循环变量更新表达式。表达式1的位置可为多个赋值表达式,各个表达式之间用逗号隔开,如:

for (i = 0, j = 0; i < 100; i++) {

statement;

}

表达式2也可以是多个条件,条件间用逻辑运算符连接,如:

for (i = 0, j = 0; i < 100 && i != 0; i++) {

statement;

}

表达式3也可以是多个变量更新,之间也用逗号隔开,如:

for (i = 0, j = 0; i < 3; i++, j++) {

statement;

}

for循环还有许多灵活的用法,这里就不一一阐述了,以后会系统的总结一下。如今只是对for的结构进行简单介绍以便引入正题。

2.for循环中循环变量的更新所带来的问题

这个问题对于一部分人来说确实不是问题,但我在这方面经常出错,相信也会有人跟我一样的,这里点出来希望大家能注意~

for (int i = 0; i <=10; i++) {

statement;

}

printf("%d\n", i);

此处跳出循环,输出i的值为11.注意,i的值为11不是10~也许这个错误看起来很低端,但是一放到程序中,就不那么低端了,而且还不容易发现。下面我就用实例来说明这一问题。

3.实例

实例一:

Description

You are given a car odometer which displays the miles traveled as an integer. The odometer has a defect, however: it proceeds from the digit 2 to the digit 4 and from the digit 7 to the digit 9, always skipping over the digit 3 and 8. This defect shows up in all positions (the one’s, the ten’s, the hundred’s, etc.). For example, if the odometer displays 15229 and the car travels one mile, odometer reading changes to 15240 (instead of 15230).

Input

Each line of input contains a positive integer in the range 1..999999999 which represents an odometer reading. (Leading zeros will not appear in the input.) The end of input is indicated by a line containing a single 0. You may assume that no odometer reading will contain the digit 3 and 8.

Output

Each line of input will produce exactly one line of output, which will contain: the odometer reading from the input, a colon, one blank space, and the actual number of miles traveled by the car.

Sample Input

15

250

0

Sample Output

15: 12

250: 160

# include

# include

int main(void) {

int odemeter, digit, number, m = 0, i; // Odemeter指显示的公里数,digit表示数字各个位上的数值,number是操作数,m是跳过的次数,i是循环变量

while (scanf("%d", &odemeter)) { // 用while实现多次输入

if (odemeter == 0) break; // 当输入为0时中止循环使得

for (i = 3; i <= odemeter; i+=5) { // 这个循环用于计算里程表跳过的值的数量,巧妙运用初始化i为3,更新时加5,使得每次循环i都是跳过的数,大大减少了循环次数

number = i; // 将待测数赋给操作数

for (int j = 1; ; j++) { // 这个循环用于连续取余以提取数字各个位上的数值,j表示取余的次数

digit = number % 10; // 取余操作

if (digit == 3 || digit == 8) { // 判断条件,m用于统计里程表跳过的数量

if (j > 1) { // 这个判断条件用于对重叠部分的处理

m += pow(10, j-1)-1; // 对于个位存在3,8时m+1,对于十位m+10,以此类推..减一是为了消除多统计一次个位所带来的影响

i += pow(10, j-1)-5; // 跳过这一段数据,避免重复

} else {

m += 1; // 取余一次时得到个位,个位有3、8时m+1

}

}

number /= 10; // 连续取余得到各个位的值的步骤

if (number != 0) // 这个if是用于中止取余循环(当已对所有位上的值进行处理时中止)

continue;

else

break;

}

}

printf("%d: %d\n", odemeter, odemeter - m); // 用里程表-跳过的数目就得到实际值

m = 0; // 因为该程序是连续输入运行,所以要对某些变量进行再次初始化以防止受到上一个循环的影响

}

return 0;

}

这道题目的意思是这个里程碑比较特别,当里程数中有3、8时会自动跳过,即里程为3他显示4,即从3->4,里程为300->400。

现要设计一个程序,能将里程碑中的数值转化为真实数值。

对于这种题,首先要寻找规律:

1.当3、8出现在个位时,跳过的数字为一个;

2.当3、8出现在十位时,跳过的数字为10个;

3.当3、8出现在百位时,跳过的数字为100个。

以此类推..

所以不难想到,用统计的方法来解决问题,即统计跳过的数目,再用里程碑上的数值-跳过的数即为真实值

首先,要将数字各个位分出来,这里需要设置一个连续取余得到各个位的循环,然后对各个位进行统计。

然而,难点就在于统计时的重叠问题。 此处我采用for循环进行数字的检测,个位+1,十位+10,百位+100以此类推,但根据for循环的初始化条件以及循环变量更新的设置,在我统计十位,百位,千位时一定会多统计一个个位。如我从298->303时,先统计个位,再统计百位,此时就发生了重叠。所以需要减去。不难发现,虽然统计百位,但并不会与十位统计重叠,只与个位有关,统计千位也亦是如此。然后在统计的同时要将统计过的数跳掉,即要对循环变量做加法处理。如此,问题就迎刃而解了。

还有一个问题,如果我for不是i+=5,而是i++,并且给i初始化为1,这样会大大增加了程序运行的时间~而采用我程序中的循环,不仅节省了时间,而且很巧妙,每个i就是跳过的数。

这道题我想指出的就是第二个for循环内的:

i += pow(10, j-1)-5;

这个步骤是用于跳过那些已经统计过的数字,我一开始错误的写法如下:

i += pow(10, j-1);

例如我是十位,我会跳过10个数字,理所应当就是i+10,但是我们要考虑的是,当结束本次循环时,for循环中还会执行循环变量更新表达式,即无缘无故多加了五,这样就会导致有数字没有被检测,所以导致了错误。这个错误也是挺隐蔽的哈哈~

实例二Erin最近学习了数组,她想通过数组实现一个蛇形方阵的打印,你可以帮她实现这个程序吗?

input:整数n,n大于2小于100

output:n*n的方阵,从方阵右上角开始以顺时针方向进行数数

如下所示

input:

3

output:

7 8 1

6 9 2

5 4 3

input:

5

output:

13 14 15 16 1

12 23 24 17 2

11 22 25 18 3

10 21 20 19 4

9 8 7 6 5

# include

int main(void) {

int count = 0, i, j, N, a[110][110]; // count用于对数组赋一段连续的数字,i,j皆为循环变量,N为方阵的边长,a数组为矩阵

scanf("%d", &N);

for (int round = 1; round <= 0.5*N; round++) { // 这里的round指顺时针的圈数,将每圈分为四个步骤:上下左右

for (j = N-round, i = round-1; i < N-round+1; i++) { // 对右进行赋值(为了能使该循环使用于任何一圈,要紧密的抓住各个条件与round的关系)

count++;

a[i][j] = count;

}

for (j = N-round-1, i = N-round; j >= round-1; j--) { // 对下进行赋值

count++;

a[i][j] = count;

}

for (i = N-round-1, j = round-1; i >= round-1; i--) { // 对左进行赋值

count++;

a[i][j] = count;

}

for (j = round-1+1, i = round-1; j < N-round; j++) { // 对上进行赋值

count++;

a[i][j] = count;

}

}

if (N % 2 != 0) { // 观察发现,奇数中间还有一个空需要另外赋值,不包含在各圈内

count++;

a[(N+1)/2-1][(N+1)/2-1] = count;

}

for (int m = 0; m < N-1; m++) // 接下来是输出该方阵,由于题目格式比较坑爹,所以接下来的输出模式也比较坑爹

for (int n = 0; n < N-1; n++) { // 这个for循环用于输出N*N矩阵的前N行

printf("%d ", a[m][n]);

if (n == N-2) // 这个if是实现当n==N-2时将最后一个元素输出以便换行

printf("%d\n", a[m][N-1]);

}

for (int m = N-1, n = 0; n < N-1; n++) { // 这个for循环用于单独输出N行,因为题目要求最后一行无需换行

printf("%d ", a[m][n]);

if (n == N-2)

printf("%d", a[m][N-1]);

}

return 0;

}

对于蛇形矩阵,我们首先要找出他的规律:

1.他的共同点是:每个矩阵都是由顺时针圈构成(N为奇数还多了个点),故要把圈数作为一个外部的大循环;

2.而且不难发现,N==3时矩阵为一圈+一点,N==4时矩阵为两圈,N==5时矩阵为两圈+一点,N==6时矩阵为三圈。我们把这一点看作是0.5圈,即是当N==3时矩阵为1.5圈,当N==4时矩阵为2圈,当N==5时矩阵为2.5圈,当N==6时矩阵为3圈,如此我们就可以设立循环的条件了。

为了使这个循环对于任意一圈都有通用性,我们需要把这个大循环内部的小循环尽量与圈数相联系起来。

这道题我想指出的就是中间的那四个for循环:

for (j = N-round, i = round-1; i < N-round+1; i++) { // 对右进行赋值(为了能使该循环使用于任何一圈,要紧密的抓住各个条件与round的关系)

count++;

a[i][j] = count;

}

for (j = N-round-1, i = N-round; j >= round-1; j--) { // 对下进行赋值

count++;

a[i][j] = count;

}

for (i = N-round-1, j = round-1; i >= round-1; i--) { // 对左进行赋值

count++;

a[i][j] = count;

}

for (j = round-1+1, i = round-1; j < N-round; j++) { // 对上进行赋值

count++;

a[i][j] = count;

}

我们发现,每个for循环都对两个值进行初始化。起初我认为,上一个循环为下一个循环默认初始化了,如对于第一个循环,看似为下一个循环带来了i值,并没有必要在给i重新赋值,但仔细一看,发现第一个循环得到i的值是N-round+1而不是下一个循环所需要的i = N-round。这个也是非常隐蔽的。

3.总结

由上面两个实例我们可以看到,for循环中循环变量的更新所带来的问题是相当隐蔽的,尤其是在长长的代码中。因此,这个问题是值得我们注意的!!!

以上内容皆为本人观点,欢迎大家指出,我们一起探讨~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值