文章目录
👍👍👍写在前面的话
❤️❤️❤️技能的精通不是一蹴而就的成功,也不是信手拈来的辉煌,需要不断的更新自己的知识,并不断的加以有效的练习来巩固自己目前所学习到的知识。读了这么有料的毒鸡汤,我们还是步正题——c语言的小练习。目的就是为了实践一下这句毒鸡汤,巩固一下目前所学习到的知识! 🐣🐣🐣practice makes perfect!🐣🐣🐣
👍👍👍👍第一题:操作符的基本认识
第 1 题(单选题)
题目名称:
下面哪个是位操作符:( )
题目内容:
A .&
B .&&
C .||
D .!
c语言之所以灵活,操作符的功劳可不小,小编理解比较深刻的就是位操作符了,直接去对二进制位进行操作,这可能是就是java这种封装的很好的语言所做不到的。废话不多说,看上一题的详情吧:
A. & 是按位与操作符,位操作符还有按位货,按位异或哦。
B. && 是逻辑与,不是按位与,这两个是有很大区别的。
C. || 是逻辑或,这个和上一个是亲兄弟;他们主要是负责逻辑的判断的,一个有意思的点是他们会发生短路现象。&&如果第一个表达式结果为假,第二个表达式则不会执行;||如果第一个表达式为真,第二个表达式为也不会执行。相信很多小伙伴也被坑过很多次了。
D. ! 是逻辑反操作符,错误
那么这些操作符具体是干什么的了?也许有很多小伙伴会很迷惑😒,后面小编会进行详细的总结哦,关注我不迷路,一起加油哦😊😊😊😊
👍👍👍👍👍第二题:基本的运算
题目名称:
下面代码的结果是:( )
#include <stdio.h> int main() { int a, b, c; a = 5; c = ++a; b = ++c, c++, ++a, a++; b += a++ + c; printf("a = %d b = %d c = %d\n:", 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
这一题主要考察了操作符的基本使用。
#include <stdio.h>
int 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 = %d\n:", 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 = %d\n:”, 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
#include<stdio.h>
/*
1.通过数字1与m,n进行&操作,如果结果为0,说明这个位置比对的数值为0,反之则为1,对两个数比对的结果进行标记,最后比对两个数的标记是否相同,如果不相同则count++
2.通过左移并赋值给c,继续上面的操作,就能达到目的*/
int 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
#include<stdio.h>
void 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
#include<stdio.h>
int 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
#include<stdio.h>
int main() {
int m = 0;
int n = 0;
scanf("%d%d", &m, &n);
//方法一:
printf("交换前:m=%d,n=%d\n", m, n);
int temp = m;
m = n;
n = temp;
printf("交换后:m=%d,n=%d\n", m, n);
//方法二
printf("交换前:m=%d,n=%d\n", m, n);
m = m + n;
n = m - n;
m = m - n;
printf("交换后:m=%d,n=%d\n", m, n);
//方法三
printf("交换前:m=%d,n=%d\n", m, n);
m = m ^ n;
n = m ^ n;
m = m ^ n;
printf("交换后:m=%d,n=%d\n", m, n);
return 0;
}
vs2019实测:
👍👍👍总结
念念不忘,必有回响,知识也一样,时常想起积累的小知识点,总有一天你会发现自己的思维居然真的能实现成代码,但在实现的路上可能会有无数bug,这也是见怪不怪的,哈哈。但总的来说就是思维不清晰或者基础知识不扎实,前者靠锻炼,后者靠积累,大家加油!最后关注小编不迷路,后期不定时更新c的知识哦!