文章目录
前言
C语言中操作符总共有10种,分别是:
算术操作符,移位操作符,位操作符,赋值操作符,单目操作符,关系操作符,逻辑操作符,条件操作符,逗号表达式,下标引用、函数调用和结构成员。
这里我挑了一些重点和易错点来说!
算术操作符,移位操作符,位操作符,复合运行算符,单目操作符、条件操作符,逗号操作符,下标引用、函数调用和结构成员
Let’s get it!
算术操作符
分为:
加:+
减:-
乘:*
除:/
取余:%
注意:
1、/
如果是整型除法,其结果是商
这里请注意,我说的是商!
如 a ÷ b = c …d,c叫做商,d叫做余数!例子: 9 / 4 = 2
如果两边有一个小数,则进行的就是小数除法,例子: 9 / 4.0 = 2.250000
2、%
代表求模运算,即余数,a % b = c,其中c的范围在 [0,b)
移位操作符
分为:
左移操作符 :<<
右移操作符:>>
在讲解移位操作符以前,这里要强调一个概念:原码反码补码
原反补
学了你就可以知道为char a=127, char b; b=a+1 这个时候你为什么b=-128
一个数在计算机内部如果是有符号数,则其最高位作为符号位,如果符号位为0,表示该数为正数,如果符号位为1,表示该数为负数。(0正1负)
以下求原反补的过程:
原码:最高位作为符号数,其余各位为数值位(0正1负)
反码:正数的反码与原码相同,负数的反码是在原码的基础上符号位不变,其余各位按位取反
补码:正数的补码与原码相同,负数的补码是在反码的基础上加1
这里看例题:
例:求+25和-25的原反补
①不考虑正负,将25转换成二进制 25D=11001B ② +25 -25 原: 00011001 10011001 反: 00011001 11100110 补: 00011001 11100111
例:求+30和-30的原反补
①30D=11110B ② +30 -30 原: 00011110 10011110 反: 00011110 11100001 补: 00011110 11100010
计算机中用的是补码,什么是补码,怎么去理解补码?
补码可以理解成一个循环。
8位二进制表示的有符号数范围是 -128~127
如果还有不懂的可以去百度一下!
这里我们回到移位操作符上
在计算机内存中,所操作的一切都是操作的 补码,补码,补码,补码!!!
重要的事情多说几遍,只是显示的时候才把补码还原回原码,然后转换成十进制
移位规则:
<<
:左移操作符:不论算术移位还是逻辑移位,均将左边的数舍弃,右边空缺位补0(左边丢弃,右边补0)
>>
:右移操作符:当进行逻辑移位时,右边位丢弃,左边空缺位补0(右边丢弃,左边补0)
再次强调,计算机操作的是补码
总结:
右移一位具有 /2效果; 右移2位 具有 /4 效果;右移三位 具有 /8 效果;(右移效果只针对正数)…
左移一位 具有x2效果;左移两位 具有x4效果;左移三位 具有x8效果;(左移效果针对所有数)…
注意:
- 移位操作不改变原值
- 移位时不能移负数位
位操作符
符号:
&
:按位与 (两个都是1才是1,否则0)
110101 101110 上面两个结果为 : 100100
|
:按位或 (只要有1就是1)
1011011 0101100 上面两个结果为: 1111111
^
:按位异或 (相同为0,相异为1)
1010001 1010101 上面两个结果为:0000100
看到这里可能有点懵,所以我出两道关于位操作符的习题
1、数字交换
给出两个数字 a 和 b ,要求除了这两个变量以外,不准创建任何变量,达到ab交换
#include <stdio.h> int main() { int a,b; scanf("%d%d",&a,&b); a = a^b; b = a^b; a = a^b; printf("%d %d", a, b); return 0; }
是不是感觉没看懂?
我们以 a = 3,b = 5为例;
假设:a ^ b = m
,那么我们反过来推导,m ^ a = b
或者 m^b = a
所以这段代码
a = a^b; b = a^b; a = a^b;
为了更好理解我暂时用am代替 a^b 出来的值a(图中m)
am = a ^ b
b = am ^ b (新得到的值其实是a,只是把值赋给了b)
a = am ^ b (am异或b,其实是异或的a,能理解吧?那么异或a得到的值就是b,所以赋值给a)
所以这样就是不要变量达到了完美的交换!
再问:数字的二进制(补码)有多少个1 ???
我们先不管二进制,假设给你一个数字a = 12334565,喊你求这个数字有几个3?
正常思路是不是 a%10 == 3 a = a/10 --> a%10 == 3 a = a/10 --> a%10 == 3 a = a/10
这样循环下去计算?
那么我们二进制也是一样 a%2 == 1 a = a/2
这样循环下去数
代码一:
#include <stdio.h> int main() { int n = 15; int count = 0; while (n) { if (n % 2 == 1) count++; n /= 2; } printf("%d", count); return 0; }
但是这样写只能算正数,所以有缺陷
下面我就介绍 位操作
我们知道只有数字1 & 1 结果才是1
步骤一: 所以我们可以通过把一个数字的二进制与00000000000000000000000000000001 相&
步骤二: 如果结果为1,代表末尾为1
步骤三: 然后把该数字右移一位,再 与 000000000000000000000000000000001 相&
以此循环…
代码二:
#include <stdio.h> int main() { int n = -1; int count = 0; for(int i = 0;i<32;i++) { if (n & 1 == 1) count++; n = n >> 1; } printf("%d", count); return 0; }
复合运算符
这个没什么讲的 ,就是组合起来操作
例子:
a = a + 3
<==> a += 3
a = a & 2
<==> a &= 2
a = a >> 1
<==>a >>= 1
单目操作符
字面理解:目,眼睛;即这个操作符只需要一个操作数
例如:!3、-4、&a、a++
这里主要讲解一下 ++
–
~
sizeof
(类型)
其中sizeof()
和strlen()
的区别请看这里:sizeof和strlen的区别
例1:sizeof
#include <stdio.h> int main() { short s = 0; int a = 10; printf("%d\n", sizeof(s = a+5)); printf("%d\n", s); return 0; }
结果是多少???
2 0
因为sizeof里面的表达式中s不参与运算
例2:++
1、前置++:即先++,再使用
代码如下:
#include <stdio.h> int main() { int a = 5; printf("%d ", a++); printf("%d", a); return 0; } //结果 5 6 //因为它先使用了a的值,然后再++
2、后置++:即先使用,再++
代码如下:
#include <stdio.h> int main() { int a = 5; printf("%d ", ++a); printf("%d", a); return 0; } //结果 6 6
--
的使用和++
一样
例3:~
~
:按位取反
问题: 假设我想把 00001011 的倒数第三个0改为1 怎么用代码弄?
很简单,我们只有把它和00000100 相 |
一下就行,那么00000100怎么来的??
数字1左移两位 1<<2
代码如下:
#include <stdio.h> int main() { /*1011代表的是 11*/ int a = 11; int ret; ret = a | (1<<2); printf("%d", ret); return 0; } //结果是 15 1111刚好就是15
再问: 那如果我想把1111改回去呢???
- 00000000 00000000 0000000 00001111
- 我们需要把它和11111111 11111111 11111111 111110111 相&
- 那11111111 11111111 11111111 111110111怎么得来的呢???
- 00000000 00000000 00000000 00000100取反得来
- 00000000 00000000 00000000 00000100而这个又是1左移两位
- 00000000 00000000 00000000 00000001(数字1)
所以逻辑是:首先 1<<2,然后取反,最后相与
代码如下:
#include <stdio.h> int main() { int a = 15; int ret; ret = a & (~(1<<2)); printf("%d", ret); return 0; } //结果是11
例4:(类型)(强制类型转换)
srand((unsigned int)time(NULL));
注意:
1.sizeof
是一个操作符,关键字,而不是函数,求的是操作数的类型长度(以字节为单位)
2.sizeof
求类型的长度时不可省略括号,求变量的长度时可以省略括号
3.!
操作符是对一个数做逻辑反操作,而~操作符是对一个数的二进制按位取反
4.前置++、前置–是先使用,再++;而后置++、后置–是先–,再使用
条件操作符
符号:exp1 ? exp2 : exp3
含义:表达式exp1如果成立,则返回表达式2的值,否则返回表达式3的值
例子: 例如我们需要把 a,b中的大值给max,小值给min
#include <stdio.h> int main() { int a = 15,b = 24; int max,min; max = a>b?a:b; min = a>b?b:a; printf("max = %d min = %d", max,min); return 0; }
逗号表达式
符号:exp1, exp2, exp3, ..., expN
含义:逗号表达式(用逗号隔开的表达式),从左往右依次执行;整个表达式的结果为最后一个表达式的结果
举例说明:
int a = 3,b = 4;
c = (a>b,a = b*4,b = a+2)
c等于多少呢? 答案:18
代码如下:
#include <stdio.h> int main() { a = get_val(); count(a); while(a>0) { a = get_val(); count(a); //语句 } return 0; } //上面这样写是不是重复了? 可以写成下面这样! #include <stdio.h> int main() { while(a = get_val(),a>0,count(a)) { //语句 } return 0; }
注意:
逗号表达式的结果虽然是最后一个表达式的结果,但不可认为与前面的表达式就无关了,
因为前面表达式可能会影响最后一个表达式的结果
下标引用、函数调用和结构成员
1、下标引用
符号:[ ]
符号说明:下标引用操作符,有两个操作数(数组名和索引值)
举例说明:
int arr[10] = {3,7,4,8,2,15,25,6,9,1}; arr[3] = 8; //[ ]:下标引用操作符,其两个操作数为arr和3
注意:下标引用共有两个操作数(数组名和索引值)
2、函数调用
符号:()
符号说明:函数调用操作符,有一个或多个操作数(函数名和参数)。
举例说明:
void test1() { printf("fine day!\n"); } void test2(char *ch) { printf("%s\n",ch); } int main() { test1(); //():函数调用操作符 test2("fine day!"); //():函数调用操作符 }
注意:函数调用操作符有一个或多个操作数
3、访问结构体成员
符号:.
符号说明:结构体对象
举例说明:
struct student { char name[10]; int age; char sex[10]; }; #include <stdio.h> int main() { struct student xiaoming = { "小明",25,"男人" }; /*访问结构成员*/ printf("姓名:%s\n", xiaoming.name); printf("年龄:%d\n", xiaoming.age); printf("性别:%s\n", xiaoming.sex); return 0; }
4、结构体指针访问
符号:->
符号说明:结构体指针
举例说明:
struct student { char name[10]; int age; char sex[10]; }; #include <stdio.h> int main() { struct student xiaoming = { "小明",25,"男人" }; struct student* people = &xiaoming; /*访问结构成员*/ printf("姓名:%s\n", people->name); printf("年龄:%d\n", people->age); printf("性别:%s\n", people->sex); return 0; }
注意:当结构体中有数组成员时,给该成员赋值用strcpy()函数,将目标串拷贝给该数组成员
see you!