目录
前言
本文介绍C语言中全部操作符,进行详细介绍,配有代码实现和运行结构以及详细介绍,欢迎点赞收藏关注。
基础知识
原码反码补码的计算
原码
十进制数据的二进制表现形式就是原码,原码最左边的一个数字就是符号位,0为正,1为负。
例如:56 -> 0 0 1 1 1 0 0 0
反码
正数的反码是其本身(等于原码),负数的反码是符号位保持不变,其余位取反。 反码的存在是为了正确计算负数,因为原码不能用于计算负数。
补码
正数的补码是其本身,负数的补码等于其反码 +1。
#include <stdio.h> int main() { int a = 15; //00000000000000000000000000001111 - 原码 //00000000000000000000000000001111 - 反码 //00000000000000000000000000001111 - 补码 int b = -15; //10000000000000000000000000001111 - 原码 //11111111111111111111111111110000 - 反码(原码的符号位不变,其他位按位取反得到的就是反码) //11111111111111111111111111110001 - 补码(反码+1就是补码) //整数在内存中存储的是补码 //计算的时候也是使用补码计算的 return 0; }
操作符分类
本文将会基于下面的操作符进行详细的介绍各个操作符的意义和用法。
算术操作符 | 移位操作符 |
位操作符 | 赋值操作符 |
单目操作符 | 关系操作符 |
逻辑操作符 | 条件操作符 |
逗号表达式 | 下标引用 |
函数调用 | 结构成员 |
算术操作符
算术操作符分类
+ | - | * | / | % |
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
例如下面程序介绍了除号是如何运用的:
#include <stdio.h> int main() { float a = 7 / 2; //3 float b = 7 / 2.0; //3.5 printf("a=%.1f b=%.1f", a, b); return 0; }
移位操作符
<< 左移操作符
>> 右移操作符
注:移位操作符的操作数只能是整数。
左移操作符
移位规则:
左边抛弃、右边补0
首先把数字改成二进制数据,再进行移位,具体实现如下图
编辑#左移操作符可以把一个数放大为原来的两倍:由于二进制的数字是有权重的,所以在计算的过程中,都是对应的数据乘2的n次方(n为该数据对应的权重的位置),于是可得出,当左移的时候,会导致权重n多了1,变成了2的n+1次方,于是每一个数字对应的权重乘2倍,整体数字也变成原来的两倍#
右移操作符
右移的时候分为两种,算术右移和逻辑右移。
算术右移
右边丢弃,左边补原来的符号位
逻辑右移
右边丢弃,左边补0
在C语言中没有明确规定到底是算术右移还是逻辑右移,在一般的编译器上采用的都是算术右移。
位操作符
按位与& | 按位或| | 按位异或^ |
这些操作符操作的数也必须是整数
按位与&
对应二进制位有0则为0,两个同时为1,才是1
下面是按位与的代码讲解:
#include <stdio.h> int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 - 补码 // int c = a & b; //& -- 对应二进制位有0则为0,两个同时为1,才是1 //00000000000000000000000000000011 //11111111111111111111111111111011 //00000000000000000000000000000011 - 补码 // printf("%d\n", c);//3 return 0; }
按位或|
按(2进制)位或 - 对应的二进制位有1则为1,两个同时为0才是0
下面是代码讲解:
#include <stdio.h> int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 - 补码 // int c = a | b; //| - 按(2进制)位或 - 对应的二进制位有1则为1,两个同时为0才是0 //00000000000000000000000000000011 //11111111111111111111111111111011 //11111111111111111111111111111011 - 补码 //11111111111111111111111111111010 //10000000000000000000000000000101 - -5 printf("%d\n", c);//3 return 0; }
按位异或^
按二进制位异或 -对应的二进制位相同为0,相异为1
下面是代码讲解:
#include <stdio.h> int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 - 补码 // int c = a ^ b; //^ - 按二进制位异或 -对应的二进制位相同为0,相异为1 //00000000000000000000000000000011 //11111111111111111111111111111011 //11111111111111111111111111111000 - 补码 //11111111111111111111111111110111 //10000000000000000000000000001000 - -8 printf("%d\n", c);//3 return 0; }
用^实现不创建临时变量完成两个数的交换(有难度)
#include <stdio.h> int main() { int a = 10; int b = 20; a = a^b; b = a^b; a = a^b; printf("a = %d b = %d\n", a, b); return 0; }
讲解:我们来具体把这个题分析一下:
编辑
由此可以看出,这样确实可以做到交换两个数的作用,那么如何进行理解?
我们可以认为,把10和20按位异或得到的是二者的“密码”,这个“密码”必须他俩同时在才能产生,那么用这个“密码”和其中一个数字进行按位异或就能得到另外一个数字,这样就达到了交换的目的。
同时,既能不溢出,又能不创建新的变量,为交换数字提供了新的解决思路。
赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
复合赋值符
+= | -= | *= | /= | %= |
>>= | <<= | &= | |= | ^= |
复合赋值符的意思如下:
#include <stdio.h> int main() { int a = 0; a = a + 1; a += 1; //写法更为简便,值和上面的相同 return 0; }
单目操作符
单目操作符介绍
! | 逻辑反操作 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作符的类型长度(字节) |
~ | 对一个数二进制按位取反 |
-- | 前置后置-- |
++ | 前置后置++ |
* | 间接访问(解引用)操作符 |
(类型) | 强制类型转换 |
sizeof 不是函数,是操作符。
sizeof 计算的是类型创建变量的大小,单位是字节。
取地址&/解引用*操作符介绍
&-取地址操作符-取出a的地址
*-解引用操作符(间接访问操作符)-单目操作符-通用pa中存放的地址,找到指向的空间(内容)
#include <stdio.h> int main() { int a = 10; //pa是指针变量 int* pa = &a;//&-取地址操作符-取出a的地址 *pa = 20;//解引用操作符(间接访问操作符)-单目操作符-通用pa中存放的地址,找到指向的空间(内容) int c = *pa; return 0; }
按位取反操作符~介绍
~ 按补码二进制位取反
#include <stdio.h> //~ 按补码二进制位取反 int main() { int a = 0; printf("%d\n", ~a);//? //00000000000000000000000000000000 //11111111111111111111111111111111 - 补码 //11111111111111111111111111111110 //10000000000000000000000000000001 -1 return 0; }
前置后置++/--
前置++:先使用再++
后置++:先++再使用
//--前置、后置-- //++前置、后置++ int main() { int a = 1; int b = a++;//后置++,先使用,后++ //b=a,a=a+1 printf("a=%d b=%d\n", a, b);//2 1 return 0; } int main() { int a = 1; int b = a--;//后置--,先使用,后-- //b=a,a=a-1 printf("a=%d b=%d\n", a, b);//0 1 return 0; } int main() { int a = 1; int b = ++a;//前置++,先++,后使用 //a=a+1,b=a printf("a=%d b=%d\n", a, b);//2 2 return 0; } int main() { int a = 1; int b = --a;//前置--,先--,后使用 //a=a-1,b=a printf("a=%d b=%d\n", a, b);//0 0 return 0; }
强制类型转换
强制进行数据类型的转换,但容易出现问题,最好少使用 !!
关系操作符
== | 用来判断相等 |
!= | 用来判断不相等 |
在编程的过程中== 和=不小心写错,导致的错误。
逻辑操作符
逻辑操作符的分类
&& | 逻辑与 |
|| | 逻辑或 |
经典例题
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; i = a++ && ++b && d++; //i = a++||++b||d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; } //程序输出的结果是什么?
当&&左边为假时,不再判断右边,直接按假处理
当 || 右边为真时,不再判断右边,直接按真处理
条件操作符
exp1 ? exp2 : exp3
若exp1为真,则执行exp2,若为假,则执行exp3
逗号表达式
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
下标引用函数调用和结构成员
[ ] 下标引用操作符
操作数:一个数组名 + 一个索引值
int arr[10];//创建数组 arr[9] = 10;//实用下标引用操作符。 //[ ]的两个操作数是arr和9。
( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
访问一个结构的成员
'.':结构体.成员名
举例如下:
struct Book { char name[30];//成员 char author[20]; float price; }; int main() { struct Book b1 = {"C Primer Plus", "xxx", 66.5f};//书 struct Book b2 = {"c语言", "xx", 88.6f};//书 printf("%s %s %.1f\n", b1.name, b1.author, b1.price); printf("%s %s %.1f\n", b2.name, b2.author, b2.price); //结构体变量.成员名 return 0; }
-> 结构体指针->成员名
举例如下:
struct Book { char name[30];//成员 char author[20]; float price; }; void Print(struct Book * p) { printf("%s %s %.1f\n", (*p).name, (*p).author, (*p).price); printf("%s %s %.1f\n", p->name, p->author, p->price); //-> //结构指针->成员名 } int main() { struct Book b1 = { "C语言", "xxx", 66.5f };//书 struct Book b2 = { "数据结构", "xx", 88.6f };//书 Print(&b1); Print(&b2); return 0; }
后续在结构体中也会继续介绍~