前言
今天给大家分享一下C语言操作符的详解,但在此之前先铺垫一下二进制和进制转换与原码、反码、补码的知识点,都有详细的图解,也希望这篇文章能对大家有所帮助,大家多多支持呀!
目录
前言
一、二进制和进制转换
1. 10进制转化为10进制
2. 2进制转化为10进制
2.1 10进制转化为2进制
3. 2进制转8进制和16进制
3.1 2进制转8进制
3.2 2进制转16进制
二、原码、反码、补码
三、操作符
1、算数操作符(+ - * / %)
2.移位操作符(<<、>>)
3.位操作符(&、|、^、~)
例题:求一个整数存储在内存中的二进制中的1的个数
4.单目操作符( ! 、- 、 +、& 、 sizeof、 ~ 、 -- 、 ++ 、 * 、 (类型) )
5.条件表达式
6.逗号表达式 (exp1, exp2, exp3, …expN)
7.下标访问操作符
8.函数调用操作符
9.结构体成员访问操作符
一、二进制和进制转换
我们所说的2进制、8进制、10进制、16进制就是数值的不同表现形式。
eg:13的2进制:1101
13的8进制:15
13的10进制:13
13的16进制:d
1. 10进制转化为10进制
eg:123
- 从个位开始,依次为0次方、1次方、2次方,位数更多的话,依次类推。
2. 2进制转化为10进制
- 2进制是由(0~1)的数字组成
- 10进制是由(0~9)的数字组成
eg:1101
- 相比十进制转化为十进制,这次的底数变为2了
- 先分别算每一项,加在一起,别忘记乘上所在位数对应的数字
2.1 10进制转化为2进制
eg:125
所得出的余数从下往上写就是转化完的二进制,为01111101,即1111101。
大家动手操作一下将它再转化为10进制!!!
注意:当数字比较小时,可以使用下图所示方法(很重要,一定要会)
如果是22呢,就为10110了
3. 2进制转8进制和16进制
- 8进制是由(0~7)的数字组成
- 16进制较为特殊,是由(0~9)、(a~f)组成的
3.1 2进制转8进制
首先,将0~7的数字各自写成2进制,最多有3个二进制位,所以像2的二进制本身是0,需要将它写成010,这个换算方法在上一节标红的注意,很实用,望大家都能掌握,在上图我已经先列出来了,因为8进制的每一位是0~7的数字,所以只需列出7个即可。然后从2进制序列中右边低位开始向左每3个2进制位会换算一个8进制位,剩余不够3个2进制位的直接换算。
如图是2进制的0110101,从最右边开始依次取三个
011:3
101:5
001:1
但是值得注意的是,换算后的153,需要写成0153 (0开头,才会被当做8进制 )
大家可以上机调试一下,153与0153是不同的,如下。
3.2 2进制转16进制
2进制转化为16进制与转化为8进制相同,是将0~9、a~f各自写成2进制,需要4个2进制位
eg:2进制的01101011
同时我也将二进制都列了出来,但是建议还是要动手算算。
所以转化为的结果为0x6b,相同的(16进制前要加上0x)
二、原码、反码、补码
- 整数的二进制形式有三种:原码、反码、补码。
- 有符号整数的三种表示方法均有符号位和数值位两部分,二进制序列中,最高的一位被当做符号位,剩余的都是数值位。
- 即正数最高位显示0,负数最高位显示1。
- 整数分为正整数与负整数。
- 在正整数中:原码=反码=补码。
- 在负整数中:
- 直接将数值按照正负数的形式翻译成二进制得到的就是原码
- 将原码的符号位不变,其他一次按位取反就是反码(1变成0,0变成1,符号位不变)
- 反码+1得到补码
例:a=3与a=-3;
注意:逢二进一,如果反码尾数是101,则变成110
整数在内存中存储的是2进制的补码
为什么数据存放的是补码?
在计算机中,数值一律用补码来表示和存储。因为使用补码可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
下图是分析的过程
三、操作符
1、算数操作符(+ - * / %)
(1)+ 、 -、 *三项与数学上用法是相同的。
(2)/ 是除法运算符,分两种情况讨论。
第一种:'/'左右两边都是整型:
#include <stdio.h>
int main()
{int a = 7, b = 2;int c = a / b;printf("%d ", c);//此时打印出来的结果为3//7除以2得出3余1//由于左右两边都是整数型,所以打印出3a = 2, b = 7;c = a / b;printf("%d ", c);//此时打印出来的结果是0return 0;
}
第二种:'/'两边有一边是浮点型:
在除法运算中,只要有一个操作数是浮点数,就是浮点数的除法运算。
#include <stdio.h>
int main()
{double c = 7.0 / 2;printf(" %lf \n", c);//此时打印出来的结果为3.5,因为其中一方由7变成了7.0,也就是浮点型c = 2 / 7.0;printf(" %.1lf \n", c);//此时打印出来的结果是0.3return 0;
}
(3)%是取模操作符
注意:取模的对象不能为0,且不能为浮点型,必须为整数!
#include <stdio.h>
int main()
{int a, b, sum;scanf_s("%d %d", &a, &b);sum = a % b;printf("%d", sum);//若a=7 b=2 则sum=1//因为7/2=3......1//而%输出的结果为余数,则为1return 0;
}
2.移位操作符(<<、>>)
注意:移位操作符的操作数只能为整数。
这里的移位表示移动的是二进制位
也就是说,移动的是存储在内存中的二进制位(补码)
(1)<<是左移操作符
移位规则:左边丢弃,右边补0。
#include <stdio.h>
int main()
{int a = 10;int b = a << 1;//10的补码为00000000 00000000 00000000 00001010//左边少一位,右边加一位即为0000000 00000000 00000000 000010100此时变为20,即b=20;printf("b=%d\n", b);printf("a=%d\n", a);return 0;
}
(2)>>是右移操作符
移位规则:
逻辑右移:与左移相似(左边用0填充,右边丢弃)
算术右移:左边用原该值的符号位填充,右边丢弃
注意:右移到底采用算术右移还是逻辑右移是取决于编译器的!通常采用的都是算术右移
#include <stdio.h>
int main()
{int a = -10;int b = a >> 1;//-10的原码为10000000 00000000 00000000 00001010//-10的反码为11111111 11111111 11111111 11110101//-10的补码为11111111 11111111 11111111 11110110//此时是右移操作符//即为11111111 11111111 11111111 11111011//反码10000000 00000000 00000000 00000100//原码10000000 00000000 00000000 00000101//即为-5//采用的是算术右移printf("b=%d\n", b);printf("a=%d\n", a);return 0;
}
注意:移位操作符不能移动负数位,a<<-1这是不行的。
3.位操作符(&、|、^、~)
&:按(二进制)位
规则见下图
#include <stdio.h>
int main()
{int a = 3;//3的补码:00000000 00000000 00000000 00000011int b = -4;//-4的原码:10000000 00000000 00000000 00000100//-4的反码:11111111 11111111 11111111 11111011//-4的补码:11111111 11111111 11111111 11111100int c = a & b;//a和b的补码的二进制位进行运算//3的补码 :00000000 00000000 00000000 00000011//-4的补码:11111111 11111111 11111111 11111100//有0才为0,两个为1才为1//即为00000000 00000000 00000000 00000000printf("%d\n", c);return 0;
| : 按(二进制)位或
规则见下图
#include <stdio.h>
int main()
{int a = 3;//3的补码:00000000 00000000 00000000 00000011int b = -4;//-4的原码:10000000 00000000 00000000 00000100//-4的反码:11111111 11111111 11111111 11111011//-4的补码:11111111 11111111 11111111 11111100int c = a | b;//a和b的补码的二进制位进行运算//3的补码 :00000000 00000000 00000000 00000011//-4的补码:11111111 11111111 11111111 11111100//只要有1就是1,两个同时为0才是0//即为11111111 11111111 1111111 11111111 //此时为补码,应该先变为反码//10000000 00000000 00000000 00000000//所以原码为10000000 00000000 00000000 00000001printf("%d\n", c);return 0;
}
^: 按(二进制)位异或
#include <stdio.h>
int main()
{int a = 3;//3的补码:00000000 00000000 00000000 00000011int b = -4;//-4的原码:10000000 00000000 00000000 00000100//-4的反码:11111111 11111111 11111111 11111011//-4的补码:11111111 11111111 11111111 11111100int c = a ^ b;//a和b的补码的二进制位进行运算//3的补码 :00000000 00000000 00000000 00000011//-4的补码:11111111 11111111 11111111 11111100//对应的二进制位,相同为0,相异位1//而3与-4的补码对应下来全部相异,所以全为1//即为11111111 11111111 1111111 11111111 //此时为补码,应该先变为反码//10000000 00000000 00000000 00000000//所以原码为10000000 00000000 00000000 00000001printf("%d\n", c);return 0;
}
~按(二进制)位取反
将补码取反即可
#include <stdio.h>
int main()
{int a = 0;printf("%d\n", ~a);//按(二进制)位取反//0的补码: 00000000 00000000 00000000 00000000//取反后变为11111111 11111111 11111111 11111111//换成原码为10000000 00000000 00000000 00000001return 0;
}
例题:求一个整数存储在内存中的二进制中的1的个数
第一种:
#include <stdio.h>
int count_bit_one( unsigned int n)
{int count = 0;while (n)
{if ((n % 2) == 1)count++;n = n / 2;
}return count;
}
int main()
{int num = 0;scanf_s("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}
第二种:
#include <stdio.h>
int count_bit_one(int n)
{int i = 0;int count = 0;for (i = 0; i < 32; i++){if (((n >> i) & 1 )== 1){count++;}}return count;
}
int main()
{int num = 0;scanf_s("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}
第三种,更为优化的方法
#include <stdio.h>
int count_bit_one(int n)
{int i = 0;int count = 0;while (n){n = n & (n - 1);count++;}return count;
}
int main()
{int num = 0;scanf_s("%d", &num);int ret = count_bit_one(num);printf("%d\n", ret);return 0;
}
相比较于第二种方法,有几个1就算几次,更加方便,不用像第二种把32位都算一遍
4.单目操作符( ! 、- 、 +、& 、 sizeof、 ~ 、 -- 、 ++ 、 * 、 (类型) )
! 逻辑反操作(把真变假,把假变真)
- 负值
+ 正值
& 取地址操作符注:与按位与不同(a&b是按位与,&a是取地址)
sizeof 求的是变量(类型)所占空间的大小,单位是字节。
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 解引用操作符(在指针中会用到)
(类型) 强制类型转换
5.条件表达式
条件运算符也叫做三目运算符,需要接受三个操作数的,形式如下:
exp1?exp2:exp3
计算逻辑:exp1为真,exp2计算,结果为表达式结果
exp1为假,exp3计算,结果为表达式结果
if(a>b)max=a;
elsemax=b;
以上代码可以改写为
max=(a>b)?a:b;
6.逗号表达式 (exp1, exp2, exp3, …expN)
#include <stdio.h>
int main()
{int a = 1;int b = 2;int c = (a > b,a=b+10,a,b=a+1)printf("%d\n", c);return 0;
}
c得出的值,不能直接算最后一项b=a+1进而得出结果为3,应该从左往右依次计算,因为前面表达式的结果,可能会影响后面的计算。
所以此题应该为13
同样的,逗号表达式也可以写在if语句中
if(a=b+4,c=5/2,d>0)
7.下标访问操作符
#include <stdio.h>
int main()
{int arr[20] = { 1,2,3,4,5 };arr[3];//数组中下标为3的元素//[]是下标引用操作符return 0;
}
操作数是arr和3。
8.函数调用操作符
接受一个或多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{printf("hehe\n");//()是函数调用操作符,printf("%d\n", 100);int ret = Add(3, 5);//Add 3 5都是操作数return 0;
}
9.结构体成员访问操作符
struct tag
{member1;member2;
} variable-list;
struct:结构体关键字
tag:结构体的标签名,可以自定义
struct tag:结构体类型
{}:里面放的是成员列表
variable-list:变量member1 , member2 是结构体成员
结构体成员的定义方式与变量和数组的定义方式相同
结构体成员,只是不能初始化。
这块结构体的内容后续会给大家补齐的,我还需要再沉淀沉淀,给大家展现一个完美的文章,嘿
今天的分享就结束啦,后续我也会强化自己,补充这篇文章,把操作符更加详细的展示