4.3 自增运算符和自减运算符
变量最常用的两种运算是“自增1“和“自减1”。可以用下面代码表示:
i = i + 1;
j = j – 1;
复合赋值运算符表示可以把语句缩短:
i += 1;
j -= 1;
C语言允许使用++(自增)和–(自减)运算符把语句缩得更短。
但是,自增/减运算符仅仅是用来缩短语句的吗?Of Course NOT!
实际上++、–运算符可以作为前缀(prefix)运算符(++i、–i),也可以作为后缀(postfix)运算符使用(i++、i–)使用,而且使用它们都会改变操作数本身的值。
比如说++i,结果就是i+1,但是它有个副作用是i自身会增加1。比如说:
i = 1;
printf("%d", ++i); // 输出2
printf("%d", i); // 输出2
算i++时,结果是i,但是随后引发i进行自增。比如说:
i = 1;
printf("%d", i++); // 输出1
printf("%d", i); // 输出2
++i意味着立即自增i,而i++意味着“先取i的原来值”,稍后再自增i。稍后是多久呢?一般是在执行下一条语句执行前进行自增。
–运算符同理可得。
i = 1;
printf("i is %d\\n", –i); //输出0
printf("i is %d\\n", i); //输出0
i = 1;
printf("i is %d\\n", i–); //输出1
printf("i is %d\\n", i); //输出0
如果同一个表达式中多次使用++或–运算符,结果会很难理解。
例如:
i = 1;
j = 2;
k = ++i + j++;
执行上述语句后,i, j, k分别是多少呢?
上面的语句等价为:
i = i + 1;
k = i + j;
j = j + 1;
最终i = 2, j = 3,k =4;
如果:
i = 1;
j = 2;
k = i++ + j++;
最终i = 2, j = 3,k = 3;
要记住:后缀++/–比一元正负号优先级高,左结合。
前缀++/–与一元正负号优先级相同,右结合。
练习题
只做了两个~
1. 给出下列程序片段的输出结果。假设i 、j 和k 都是int 型变量。
(a)
i = 5; j = 3;
printf("%d %d", i / j, i % j);
分析:5 / 3,除号两边操作数都是整数,所以结果也是整数,所以5 / 3 = 1
5 % 3,余数是2。
所以结果是1 2。
验证:
#include <stdio.h>
int main()
{
int i = 5, j = 3;
printf("%d %d", i / j, i % j);
return 0;
}
(b)
i = 2; j = 3;
printf("%d", (i + 10) % j);
分析:i + 10的结果是2+10=12,j的值是3。所以最后算式等价于12 % 3 为0,能被3整除。
所以输出0。
验证:
#include <stdio.h>
int main()
{
int i = 2, j = 3;
printf("%d", (i + 10) % j);
return 0;
}
(c)
i = 7; j = 8; k = 9;
printf("%d", (i + 10) % k / j);
分析:将变量值一次带入,得到(7 + 10) % 9 / 8。化简17 % 9 / 8,这里是先算%还是先算/呢?经过查询优先级,是先算%再算/。所以得到8 / 8 = 1。
最后输出 1。
验证:
#include <stdio.h>
int main()
{
int i = 7, j = 8, k = 9;
printf("%d", (i + 10) % k / j);
return 0;
}
(d)
i = 1; j = 2; k = 3;
printf("%d", (i + 5) % (j + 2) / k);
分析:直接变量值带入,(1 + 5) % (2 + 2) / 3 = 6 % 4 / 3,算得2 / 3 = 0
输出 0
验证:
#include <stdio.h>
int main()
{
int i = 1, j = 2, k = 3;
printf("%d", (i + 5) % (j + 2) / k);
return 0;
}
*2. 如果i 和 j 都是正整数,(-i) / j 的值和-(i / j) 的值是否总一样?验证你的答案。
如果i,j都为int类型,它们的运算跟数学上是一致的,都是一样的。
若i或j为unsignd int类型,一个可能为负值,另一个变成很大的正数,就不一样了。
编程题
1. 编写一个程序,要求用户输入一个两位数,然后按数位的逆序打印出这个数。程序会话应类似下面这样:
Enter a two-digit number: 28
The reversal is: 82
用%d 读入两位数,然后分解成两个数字。提示 :如果n 是整数,那么n % 10 是个位数,而n / 10 则是移除个位数后剩下的数。
分析:n%10保留数字n的第一个尾数,n/10去掉数字n的第一个尾数。
那么推得:n%保留数字n的a个尾数,n/
去掉数字n的a个尾数。
而且数字的组合abc(十进制) = 100 * a + 10 * b + c。
那么可以写得代码:
#include <stdio.h>
int main()
{
int n, a, b, i;
printf("Enter a two-digit number: ");
scanf("%d", &n);
a = n / 10;
b = n % 10;
i = 10 * b + a;
printf("The reversal is: %d", i);
return 0;
}
2. 扩展上题中的程序使其可以处理3位数。
分析:先去尾数再保留尾数,或先保留尾数再去尾数。
方法1(推荐):
#include <stdio.h>
int main()
{
int n, a, b, c, i;
printf("Enter a three-digit number: ");
scanf("%d", &n);
a = n / 100;
b = n / 10 % 10;
c = n % 10;
i = 100 * c + 10 * b + a;
printf("The reversal is: %d", i);
return 0;
}
方法2:
#include <stdio.h>
int main()
{
int n, a, b, c, i;
printf("Enter a three-digit number: ");
scanf("%d", &n);
a = n % 1000 / 100;
b = n % 100 / 10;
c = n % 10 / 1;
i = 100 * c + 10 * b + a;
printf("The reversal is: %d", i);
return 0;
}
3. 重新编写编程题2中的程序,使新程序不需要利用算术分割就可以显示出3位数的逆序。提示 :参考4.1节的upc.c 程序。
分析:这个就是利用scanf的%1d格式符,一个个的存到变量再组合。
#include <stdio.h>
int main()
{
int n, a, b, c, i;
printf("Enter a three-digit number: ");
scanf("%1d%1d%1d", &a, &b, &c);
i = 100 * c + 10 * b + a;
printf("The reversal is: %d", i);
return 0;
}
4. 编写一个程序,读入用户输入的整数并按八进制(基数为8)显示出来:
Enter a number between 0 and 32767: 1953
In octal, your number is: 03641
输出应为5位数,即便不需要这么多数位也要如此。提示 :要把一个数转换成八进制,首先将其除以8,所得的余数是八进制数的最后一位(本例中为1);然后把原始的数除以8,对除法结果重复上述过程,得到倒数第二位。
分析:经典的除R取余法。
#include <stdio.h>
int main()
{
int n, a, b, c, d, e;
printf("Enter a number between 0 and 32767: ");
scanf("%d", &n);
printf("In octal, your number is: " );
a = n % 8; n = n / 8;
b = n % 8; n = n / 8;
c = n % 8; n = n / 8;
d = n % 8; n = n / 8;
e = n % 8; n = n / 8;
printf("%d%d%d%d%d", e, d, c, b, a);
return 0;
}
5. 重写4.1节的upc.c 程序,使用户可以一次输入11位数字,而不用先录入1位,再录入5位,最后再录入5位。
Enter the first 11 digits of a UPC: 01380015173
Check digit: 5
分析:合起来就行。
/* Computes a Universal Product Code check digit */
#include <stdio.h>
int main(void)
{
int d, i1, i2, i3, i4, i5, j1, j2, j3, j4, j5, first_sum, second_sum, total;
printf("Enter the first 11 digits of a UPC: ");
scanf("%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d", &d, &i1, &i2, &i3, &i4, &i5, &j1, &j2, &j3, &j4, &j5);
first_sum = d + i2 + i4 + j1 + j3 + j5;
second_sum = i1 + i3 + i5 + j2 + j4;
total = 3 * first_sum + second_sum;
printf("Check digit: %d\\n", 9 – ((total – 1) % 10));
return 0;
}
6. 欧洲国家不使用北美的12位通用产品代码(UPC),而使用13位的欧洲商品编码(European Article Number, EAN)。跟UPC一样,每个EAN码的最后也有一个校验位。计算校验位的方法也类似:首先把第2位、第4位、第6位、第8位、第10位和第12位数字相加;然后把第1位、第3位、第5位、第7位、第9位和第11位数字相加;接着把第一次加法的结果乘以3,再和第二次加法的结果相加;随后,再把上述结果减去1;相减后的结果除以10取余数;最后用9减去上一步骤中得到的余数。以Güllüoglu Turkish Delight Pistachio & Coconut为例,其EAN码为8691484260008。第一个和为6+1+8+2+0+0=17,第二个和为8+9+4+4+6+0=31。第一个和乘以3再加上第二个和得到82,减1得到81。这个结果除以10的余数是1,再用9减去余数得到8,与原始编码的最后一位一致。请修改4.1节的upc.c 程序以计算EAN的校验位。用户把EAN的前12位当作一个数输入:
Enter the first 12 digits of an EAN: 869148426000
Check digit: 8
分析:这题上篇文章做过,但题目给出的算法似乎又有点不同。尝试一下写代码:
#include <stdio.h>
int main(void)
{
int i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12;
printf("请输入商品条形码(不输入最后一位):");
scanf("%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d",
&i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8, &i9, &i10, &i11, &i12);
int A, B, C, check;
A = (i12 + i10 + i8 + i6 + i4 + i2) * 3;
B = (i11 + i9 + i7 + i5 + i3 + i1);
C = (A + B – 1) % 10;
check = 9 – C;
printf("Check digit(最后一位是): %d\\n", check);
return 0;
}
明天开始学习第五章选择语句。
评论前必须登录!
注册