初识C语言1(操作符详解)
目录
1.算术操作符
+ - * / %
算术操作符的话有:加号(+),减号(-),乘号(*),除号(/),取模(%)。大家应该都非常的熟悉,也比较简单,给大家稍微说一下,其中的一些需要注意的地方我给大家讲一下。加号(+):进行加法计算,可以整数相加,字符相加,浮点数相加,减号(-),乘号(*)也一样。
除号(/),我们来看下面的小例子:
3/5打印的结果为0。这里就要说一下结果为什么是0,不应该是0.6嘛。
3/5,除号(/)两边的两个操作数是整数,所以这里进行的是整数除法,3/5这里是商0余3,不够除,商0,余了个3。如果把3换成6,6/5打印的结果就是1,这里就是商1余1。这就叫做整数除法
如果我们不想要打印整数,想要的是浮点数呢,我们来看下面的小例子:
想要打印出浮点数,除号两端的操作数最少有一个是浮点数,这样执行的就是浮点数的除法。我们就能得到0.6的结果。
因为除号两端有一个是浮点数,所以我们改成float类型。打印的是浮点数,所以用%f。
取模(%):
取模(%)操作符的两个操作数必须为整数。返回的是整除之后的余数。
上面例子打印的结果是 1 。
7 % 3求的就是7 / 3的余数,7 / 3的结果是商2余1,所以打印的结果就是1。
2.移位操作符
(<<) 左移操作符 (>>) 右移操作符
(<<) 左移操作符,我们来看下面例子:
上面把a向左移动移动一位就是把a的二进制序列向左移动一位。
左移操作的移动规则就是:左边丢弃,右边补0。
所以向左移动一位后求得b的值为4。
(>>) 右移操作符
右移操作符分两种:
算术右移,右边丢弃,左边补原符号位。
逻辑右移,右边丢弃,左边补0。
我们来看下面例子:
a的二进制序列:
00000000 00000000 00000000 00000010
向右移动一位的:
00000000 00000000 00000000 00000001
因为a是正数,所以不管是逻辑运算还是算术运算,左边补的都是0。所以得到的b值就是1。
我们可以测试一下当前编译器是采用的算术右移还是逻辑右移,我们用 -1 来测试一下:
负数:-1
我们存放到内存中
说到这里必须给大家交代一个知识点,
整数的二进制表示形式有三种,分别是原码,反码,补码。
原码:直接根据数值写出的二进制序列就是原码。
反码:原码的符号位不变,其他位按位取反就是反码。
补码:反码+1,就是补码。
-1,我们放到a里面怎么放呢,首先我们要写出它的原码,再写出它的反码,再写出它的补码。-1要存放到内存中的话,是以补码的形式存放的。(此算法是针对负数的,正整数原反补相同)。
-1的二进制序列:
原码:10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110
补码:11111111 11111111 11111111 11111111
现在我们把-1向右移动一位,如果你当前的编译器采用的是算术右移,右边丢弃,左边补原符号位,那么你得到值就还是-1,如果是逻辑右移,右边丢弃,左边补0,那么你得到的值就是01111111 11111111 11111111 11111111,这是一个比较大的值,大家私下里可以自己算一下。
还有一点要跟大家说一下,我们把a向右移动一位,a的值是不变的,我们只是把a向右移动一位的值放到b里面,a的值是不变的。
3.位操作符
(&)按位与 (|)按位或 (^)按位异或
(&)按位与,我们看下面的例子:
int a = 3;
int b = 5;
//& → 就是按(2进制)位与
//a → 00000000000000000000000000000011
//b → 00000000000000000000000000000101
//对应的2进制位按位与,只要有0,结果就是0,两个都为1结果才是1
//按位与后的结果 → 00000000000000000000000000000001
int c = a & b;
printf("%dn", c);//打印结果是1
(|)按位或,我们看下面的例子:
int a = 3;
int b = 5;
//| → 按(2进制)位或
//a → 00000000000000000000000000000011
//b → 00000000000000000000000000000101
//对应的2进制位按位或,只要有1有1结果则为1,两个同时为0结果则为0
//按位或后的结果 → 00000000000000000000000000000111
int c = a | b;
printf("%dn", c);//打印结果是7
(^)按位异或,我们看下面的例子:
int a = 3;
int b = 5;
//^ → 按(2进制)位异或
//a → 00000000000000000000000000000011
//b → 00000000000000000000000000000101
//对应的2进制位按位异或,相同为0,相异为1
//按位异或后的结果 → 00000000000000000000000000000110
int c = a ^ b;
printf("%dn", c);//打印结果是6
这三个操作符的操作数必须都是整数。
4.赋值操作符
(=)赋值操作符 ( += -= *= /= %= <<= >>= &= |= ^= )复合赋值操作符
(=)赋值操作符
一个等号(=)叫赋值,两个等号(==)叫判断
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己
重新赋值。
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0; salary = 20000.0;//使用赋值操作符赋值。
赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值(不建议写成这种形式,拆开写,简洁大方,通俗易懂)
x = y+1;
a = x;
复合赋值符
int x = 10;
x = x+10;//简化一下可以写成下面的方式
x += 10;//复合赋值
这两种写法是完全等价的两种写法。
5.单目操作符
! 逻辑反操作
- 负值
+ 正值
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
(!)逻辑反操作符
操作数只有一个操作符叫单目操作符
我们看一个例子:
这里只会打印hehe,因为!flag为假。
(!) 逻辑反操作符可以让假变成真,真变成假。
我们可以测试一下,
int flag = 0;为假
printf("%d", !flag);我们打印的结果就是1。
反之为flag为1,!flag打印结果为0。
真我们用1表示,假我们用0表示。
- 负值
正负的意思。看小例子:
a打印的结果就是-10。
+ 正值
a = +a这种写法是可以忽略的,没什么用,所以就省略了。
sizeof 操作数的类型长度(以字节为单位)
计算一个类型或者变量所占内存的大小。
举例说明:
a的类型是int,sizeof(a)可以计算a所占空间的大小,单位是字节。上例两种写法是等价的,打印结果都是4。我们也可以写成 sizeof a 的形式,把圆括号去掉。这就表面sizeof是一个操作符,不是函数。
sizeof也可以计算数组的大小,看例子
数组arr是char类型,char类型所占空间的大小是1个字节,arr数组有十个元素,所以上面打印的结果是10,单位是字节。上面两种打印写法是等价的。
我们再看一个列子:
大家觉的上面例子打印的值应该是多少?就不和大家卖关子了,打印的结果分别为2和5。为什么呢,a + 2虽然是个int类型的值,但是s不会因为放了这样一个值空间就变大,而sizeof求的是s所占空间字节的大小,所以第一个打印结果是2。为什么第二个是5呢,因为sizeof内部中放的表达式是不参与运算的。所以a + 2的值不会放到s里,s的值没变。
~ 对一个数的二进制按位取反
看例子:
// ~ 对一个数的二进制位按位取反
//负数在内存中存的是补码,
//-1的补码1111111111111111111111111111111
// ~按位取反
// a的值11111111111111111111111111111111
// b的值00000000000000000000000000000000
int a = -1;
int b = ~a;
printf("%dn", b);//结果为0
(本文中的二进制位数都是32位,如果出现少一位或者多一位的现象纯粹是作者手抖)
-- 前置、后置--
++ 前置、后置++
看例子:
前置++,先++,后使用。.
后置++,先使用,后++。
-- 前置、后置-- 同理。
再举个例子:
6. 关系操作符
> 大于运算符
>= 大于等于运算符
< 小于运算符
<= 小于等于运算符
!= 用于测试“不相等”
== 用于测试“相等”
看下面一些例子:
a == b;
a != b;
a < b;
a > b;
a <= b;
a >= b;
关系表达式通常返回0
或1
,表示真伪。C 语言中,0
表示伪,所有非零值表示真。比如,20 > 12
返回1
,12 > 20
返回0
。注意,相等运算符==
与赋值运算符=
是两个不一样的运算符,不要混淆。
关系表达式常用于if
或while
结构。
f (x == 3) {
printf("x is 3.n");
}
有时候,可能会不小心写出下面的代码,它可以运行,但很容易出现意料之外的结果。
if (x = 3) ...
上面示例中,原意是x == 3
,但是不小心写成x = 3
。这个式子表示对变量x
赋值3
,它的返回值为3
,所以if
判断总是为真。
为了防止出现这种错误,有些人喜欢变量写在等号的右边。
if (3 == x) ...
这样的话,如果把==
误写成=
,编译器就会报错。
// 报错
if (3 = x) ...
另一个需要避免的错误是,多个关系运算符不宜连用。
i < j < k
上面示例中,连续使用两个小于运算符。这是合法表达式,不会报错,但是通常达不到想要的结果,即不是保证变量j
的值在i
和k
之间。因为表示运算符是从左到右计算,所以实际执行的是下面的表达式。
(i < j) < k
上面式子中,i < j
返回0
或1
,所以最终是0
或1
与变量k
进行比较。如果想要判断变量j
的值是否在i
和k
之间,应该使用下面的写法。
i < j && j < k
7. 逻辑操作符
&& 逻辑与
|| 逻辑或
看下面例子:
&&
:与运算符(两侧的表达式都为真,则为真,否则为伪)。
||
:或运算符(两侧至少有一个表达式为真,则为真,否则为伪)。
对于逻辑运算符来说,任何非零值都表示真,零值表示伪。比如,5 || 0
会返回1
,5 && 0
会返回0
逻辑运算符还有一个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证的。如果左边的表达式满足逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为“短路”。
举个例子:
因为a++是后置++,所以先使用,后++,a为0,根据短路效应,&&前面的值如果为假后面的表达式就不进行计算了,所以最终的结果为1,2,3,4。
这次我们把a的值改为1,因为a为真,根据短路效应,|| 前面的值如果为真后面的表达式就不进行计算了,所以最终的结果为2,3,3,5。
8.条件操作符
exp1 ? exp2 : exp3 exp(表达式的意思)
条件操纵符也叫三目操作符。
( exp1 ? exp2 : exp3 )整体也是一个表达式。
意思为:如果表达式1的结果为真,表达式2计算,表达式3不算,表达式2的结果是整个表达式的结果。如果表达式1的结果为假,表达式2不算,表达式3计算,表达式3的结果是整个表达式的结果。我们来看下面例子:
9.逗号表达式
逗号表达式,从左向右依次计算,整个表达式的结果是最后一个表达式的结果。
所以说c=5,a=a+3结果为6,b=a-4结果为2,c+=5结果为10,所以我们打印的结果d为10。不能只算最后一个表达式的结果,一定要从左向右一次进行计算。