- 写在前面的话
- 第一题:操作符的基本认识
- 第二题:基本的运算
- 第三题:求两个数二进制中不同位的个数
- 第四题:打印整数二进制的奇数位和偶数位
- 第五题:统计二进制中1的个数
- 第六题:交换两个变量(不创建临时变量)
- 总结
第一题:操作符的基本认识❤️❤️❤️技能的精通不是一蹴而就的成功,也不是信手拈来的辉煌,需要不断的更新自己的知识,并不断的加以有效的练习来巩固自己目前所学习到的知识。读了这么有料的毒鸡汤,我们还是步正题——c语言的小练习。目的就是为了实践一下这句毒鸡汤,巩固一下目前所学习到的知识! practice makes perfect!
第 1 题(单选题)
题目名称:
下面哪个是位操作符:( )
题目内容:
A .&
B .&&
C .||
D .!
c语言之所以灵活,操作符的功劳可不小,小编理解比较深刻的就是位操作符了,直接去对二进制位进行操作,这可能是就是java这种封装的很好的语言所做不到的。废话不多说,看上一题的详情吧:
A. & 是按位与操作符,位操作符还有按位货,按位异或哦。
B. && 是逻辑与,不是按位与,这两个是有很大区别的。
C. || 是逻辑或,这个和上一个是亲兄弟;他们主要是负责逻辑的判断的,一个有意思的点是他们会发生短路现象。&&如果第一个表达式结果为假,第二个表达式则不会执行;||如果第一个表达式为真,第二个表达式为也不会执行。相信很多小伙伴也被坑过很多次了。
D. ! 是逻辑反操作符,错误
那么这些操作符具体是干什么的了?也许有很多小伙伴会很迷惑,后面小编会进行详细的总结哦,关注我不迷路,一起加油哦
第二题:基本的运算题目名称:
下面代码的结果是:( )
#includeint main() { int a, b, c; a = 5; c = ++a; b = ++c, c++, ++a, a++; b += a++ + c; printf("a = %d b = %d c = %dn:", a, b, c); return 0; } 题目内容:
A .a = 8 b = 23 c = 8
B .a = 9 b= 23 c = 8
C .a = 9 b = 25 c = 8
D .a = 9 b = 24 c = 8
这一题主要考察了操作符的基本使用。
#includeint main() { int a, b, c; a = 5; c = ++a;// ++a:加给a+1,结果为6,用加完之后的结果给c赋值,因此:a = 6 c = 6 b = ++c, c++, ++a, a++; // 逗号表达式的优先级,最低,这里先算b=++c, b得到的是++c后的结果,b是7 // b=++c 和后边的构成逗号表达式,依次从左向右计算的。 // 表达式结束时,c++和,++a,a++会给a+2,给c加1,此时c:8,a:8,b:7 b += a++ + c; // a先和c加,结果为16,后缀++优先级比+=高,所以a先加1,在加上b的值7,b的结果为23,a的值为9 printf("a = %d b = %d c = %dn:", a, b, c); // a:9, b:23, c:8 return 0; }
友情提示:如果记不得a,b,c分别的值,可以列出一个表格,在每执行一次之后就更新值,这样是一定不会错的;
| 操作 | a | b | c |
|---|---|---|---|
| main函数栈帧创建 | 未创建 | 未创建 | 未创建 |
| int a, b, c; | 随机值 | 随机值 | 随机值 |
| a = 5; | 5 | 随机值 | 随机值 |
| c = ++a;(++a:加给a+1,结果为6,用加完之后的结果给c赋值) | 6 | 随机值 | 6 |
| b = ++c | 6 | 7 | 7 |
| c++ | 6 | 7 | 8 |
| ++a | 7 | 7 | 8 |
| a++ | 8 | 7 | 8 |
| b += a++ + c; | 9 | 23 | 8 |
| printf(“a = %d b = %d c = %dn:”, a, b, c); | 9 | 23 | 8 |
| return 0; | 销毁 | 销毁 | 销毁 |
怎么样,这样看起来是不是清晰的一塌糊涂,当然提及到了一些函数栈帧的知识,感兴趣的小伙伴可以研究一下哦。
第三题:求两个数二进制中不同位的个数题目名称:
求两个数二进制中不同位的个数
题目内容:
编程实现:两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?
输入例子:
1999 2299
输出例子:7
冥思一想,可知万千道来,当然我采用的是大家都能想到比较挫的方法,整体思路如下:
1.通过数字1不断左移与m,n进行&操作,如果结果为0,说明这个位置比对的数值为0,反之则为1,对两个数比对的结果进行标记,最后比对两个数的标记是否相同,如果不相同则count++ 2.通过左移并赋值给c,继续上面的操作,就能达到目的,可能这方法比较挫,相信小伙伴们还有其它更加巧妙的方法。
具体代码实现:
#define _CRT_SECURE_NO_WARNINGS #includeint differences(int m, int n) { int count = 0; int c = 1; for (int i = 0; i < 32; i++) { int markm = 0; int markn = 0; if ((m & c) == 0) { markm = 0; } else { markm = 1; } if ((n & c) == 0) { markn = 0; } else { markn = 1; } if (markm == markn) { c <<= 1; } else { c <<=1; count++; } } return count; } int main() { int m = 0; int n = 0; scanf("%d%d", &m, &n); int ret=differences(m, n); printf("%d和%d在32位环境下二进制的不同位数位:%d",m,n,ret); return 0; }
vs2019测试结果:
可以看到我们的输出结果是和题目吻合的。当然还有其它的思路,比如:
1. 先将m和n进行按位异或,此时m和n相同的二进制比特位清零,不同的二进制比特位为1 2. 统计异或完成后结果的二进制比特位中有多少个1即可
这个思路好像更简单哦,代码实现起来也更easy,小编则是用到了更多的一些位操作知识,才能达到熟悉知识的操作,后面这种思路大家可以动手实践一下哦!我试过也是能够实现的,相信你们比我更快的写出来的,保持空杯心态,才能学到更多的细节,加油!(●’◡’●)
第四题:打印整数二进制的奇数位和偶数位题目名称:
打印整数二进制的奇数位和偶数位
题目内容:
获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列
和上题思路大致类似,只不过这一次我们是把二进制数字判断以后分别存进不同的数组中。
1.通过1与该对应位置进行&操作,如果等于0,则存0,如果等于1,则存1;
2.右移1并复制给c,继续执行1的操作,并设置flag,判断是应该存在哪个数组中;
3.判断完成,输出两个数组中的值;(逆序输出)
代码实现:
#define _CRT_SECURE_NO_WARNINGS #includevoid showarr(int* arr, int sz) { for (int i = sz-1;i>=0;i--) { printf("%d", arr[i]); } printf("n"); } int main() { int k = 0; int c = 1; int m = 0; int n = 0; int jishu[50]; int oushu[50]; printf("请输入要检查的值:n"); scanf("%d", &k); int flag = 0;//定义标记,用来区分存入什么数组中 for (int i = 0;i < 32;i++) { if (flag == 0) { if ((k & c) == 0) { jishu[m++] = 0; c <<= 1; } else { jishu[m++] = 1; c <<= 1; } flag = 1; continue; } if (flag == 1) { if ((k & c) == 0) { oushu[n++] = 0; c <<= 1; } else { oushu[n++] = 1; c <<= 1; } flag = 0; } } printf("奇数:"); showarr(jishu, m); printf("偶数:"); showarr(oushu, n); return 0; }
vs2019实测:
做多了,就会发现一个技巧,c<<=1是个万金油了,位操作就会想到它了,这难道条件反射了吗?哈哈,开个玩笑,来看下一题哦。
第五题:统计二进制中1的个数题目名称:
统计二进制中1的个数
题目内容:
写一个函数返回参数二进制中 1 的个数。
比如: 15 0000 1111 4 个 1
这里的方法就很多了,提供一下几种方法吧:
| 方法一: | |
|---|---|
| 思路: | |
| 循环进行以下操作,直到n被缩减为0: | |
| 1. 用该数据模2,检测其是否能够被2整除 | |
| 2. 可以:则该数据对应二进制比特位的最低位一定是0,否则是1,如果是1给计数加1 | |
| 3. 如果n不等于0时,继续1 |
上述方法缺陷:进行了大量的取模以及除法运算,取模和除法运算的效率本来就比较低。
| 方法二思路: | |
|---|---|
| 一个int类型的数据,对应的二进制一共有32个比特位,可以采用位运算的方式一位一位的检测, |
方法二优点:用位操作代替取模和除法运算,效率稍微比较高
缺陷:不论是什么数据,循环都要执行32次
| 方法三: | |
|---|---|
| 思路:采用相邻的两个数据进行按位与运算 |
我采用的是第二种思路写的代码,毕竟前面说到已经形成条件反射的想到了c<<=1。
代码实现:
#define _CRT_SECURE_NO_WARNINGS #includeint many_times(int n) { int c = 1; int count = 0; for (int i = 0;i < 32;i++) { if ((n & c) == 0) { c <<= 1; } else { count++; c <<= 1; } } return count; } int main() { int n = 0; printf("请输入一个数字:n"); scanf("%d", &n); int ret = many_times(n); printf("%d中有%d个1!", n, ret); return 0; }
vs2019实测:
第六题:交换两个变量(不创建临时变量)题目名称:
交换两个变量(不创建临时变量)
题目内容:
不允许创建临时变量,交换两个整数的内容
其实这道题是有两种解法的,我们日常使用的
int temp = m; m = n; n = temp;
这种样式的代码,应为创建了临时变量temp,不满足题目的要求;有小伙伴可能会提出这样的代码:
m = m + n; n = m - n; m = m - n;
这样确实能够实现交换并且满足题目要求,我们来看一下:
| 操作 | m | n |
|---|---|---|
| m = m + n; | m+n | n |
| n = m - n; | m+n | m |
| m = m - n; | n | m |
看懂了吗,这样实现简单的加减操作之后,也能实现交换操作,但是这样真的没啥问题吗,之前学过的二分查找哪里我们寻找中间坐标时,为什么不采用(left+right)/2这样的形式了,应为一个有符号的int表示的范围是-2* 31~2* 31-1这样的数字,当我们使用直接相加的方式可能会造成溢出的情况,就会引起数据丢失。这里也是一样的道理哦,所以这种方法存在一定的风险。
也许下一张就是王牌哦:
m = m ^ n; n = m ^ n; m = m ^ n;
这里的思想和2感觉是有点类似的,把m^n存到m中,可以理解成把m,和n的差异存起来;
| 操作 | m | n |
|---|---|---|
| m = m^ n; | m和n的差异 | n |
| n = m ^n; | m和n的差异 | m |
| m = m ^ n; | n | m |
第二步m和n的差异在去和n进行^运算,差异再和n去找差异,是不是就是m了,再赋值给m;下一步同理!
代码实现:
#define _CRT_SECURE_NO_WARNINGS #includeint main() { int m = 0; int n = 0; scanf("%d%d", &m, &n); //方法一: printf("交换前:m=%d,n=%dn", m, n); int temp = m; m = n; n = temp; printf("交换后:m=%d,n=%dn", m, n); //方法二 printf("交换前:m=%d,n=%dn", m, n); m = m + n; n = m - n; m = m - n; printf("交换后:m=%d,n=%dn", m, n); //方法三 printf("交换前:m=%d,n=%dn", m, n); m = m ^ n; n = m ^ n; m = m ^ n; printf("交换后:m=%d,n=%dn", m, n); return 0; }
vs2019实测:
总结念念不忘,必有回响,知识也一样,时常想起积累的小知识点,总有一天你会发现自己的思维居然真的能实现成代码,但在实现的路上可能会有无数bug,这也是见怪不怪的,哈哈。但总的来说就是思维不清晰或者基础知识不扎实,前者靠锻炼,后者靠积累,大家加油!最后关注小编不迷路,后期不定时更新c的知识哦!



