纸上得来终觉浅, 绝知此事要躬行。
主页:June-Frost
专栏:C语言
该篇将详细介绍各种操作符的功能。
操作符是编程中表示操作的符号或符号组合。它们用于执行算术、逻辑、比较和其他操作。
操作符可以分为这几类:算术操作符;移位操作符;位操作符;赋值操作符;单目操作符;关系操作符;逻辑操作符;条件操作符;逗号表达式;下标引用、函数调用和结构成员。下面将会一 一介绍这些操作符。
+ - * / %
前面的 + - * 就和数学的逻辑一样,都可以作用于整数和浮点数。
/ 计算后的结果是商。并且有两种除法,一个是整数除法,一个是浮点数除法。
在运用除法时,除数不可以为0。例如: int n = 0;int ret = 6 / n;
。
% 被称为取模操作符,也就是算余数。但是需要注意的是,它的操作数只能是整数,不可以是浮点数。 例如:可以 5%2,但是不可以 5% 2.0 。
>>(右移) <<(左移)
注意:移位操作符的操作数只能是整数。
在计算机中,计算机能够处理的是二进制信息,即由0和1组成的序列,这里的移位操作其实就是在移动二进制。在计算机中将一个十进制的数字转化为二进制,会出现3种不同的表现形式:原码,反码,补码。
注意:
正整数的原码,反码,补码是相同的。
负整数的原码,反码,补码不同,需要计算得到。
例如:15 (十进制)它的二进制其实就是 1111。
但是,15的默认类型为 int ,int 类型是4个字节,即32个bit,一个二进制位占1个bit,所以我们需要向前补充0。需要注意这里规定了最高位是符号位:0表示正,1表示负数。
例如:
反码:
正整数的原码,反码,补码都相同。
负整数的反码:原码的符号位不变,其它位按位取反(1变为0,0变为1)。
补码:
正整数的原码,反码,补码都相同。
负整数的补码:反码+1。
1.整数在内存中存储的是补码。
2.计算的时候是使用补码来计算的。
所以这里移动的就是二进制的补码。
右移分为两种:
- 算术右移:右边丢弃,左边补原来的符号位。
- 逻辑右移:右边丢弃,左边直接补0。
C语言没有明确规定使用哪种右移方式,但是一般编译器(例如:VS)上采用的是算术右移。
使用例子:
因为原码是根据正负直接写出的二进制序列,所以打印的时候是需要原码的。
左移只有一种:左边丢弃,右边补0。
使用例子:
根据这些例子,我们可以发现一些信息:
- 右移的操作,可以看成一个数据除以2后,再向下取整。
- 左移的操作,可以看成一个数据乘2。
⚠警告:
1.对于移位运算符,不要移动负数位,这个是标准未定义行为,例如:a>>-1
,这种表达式的结果是不可预料的,甚至不同的编译器处理的行为都是不一样的。
2.需要在合法范围内移位。
3.移位操作不会改变自身的值,例如a>>1
,a本身的值不会改变。
这里操作的也是二进制补码。
& ^ |
注意:操作数必须为整数
^ 按位异或 拥有一些特性:
1.一个变量 异或本身得到的值是0。例如:a ^ a
结果为0。
2.一个变量 异或0 得到的值是变量本身的值。例如:int a = 5; printf("%d", a ^ 0);
得到的值是 5 。
3.按位异或满足交换律,a^ a^ b
与a^ b^ a
得到的值是一样的。
通过这些特性,我们可以完成不创建临时变量(第三个变量),实现两个整数的交换。
#include
int main()
{
int a = 2;
int b = 4;
printf("交换前:a = %d b = %d\n", a, b);
//交换
a = a ^ b;
b = a ^ b;
a = a ^ b;
//
printf("交换后:a = %d b = %d\n", a, b);
return 0;
}
= += -= *= /= &= ^= |= >>= <<=
a = 0;
. int a = 0;
int x = 1;
int y = 2;
a = x = y + 1;//从右至左连续赋值
虽然这种方式语法是允许的,但是分开写更加清晰,更易于调试。
int a = 0;
int x = 1;
int y = 2;
x = y + 1;
a = x;
a = a + 1;
和a+=1;
意思是一样的。像-= ,*= 等,其实都是类似的。只有一个操作数
! - + & sizeof ~ – ++ * (类型)
单目操作符 | |
---|---|
! | 逻辑反操作 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作数的类型长度 |
~ | 对一个数的二进制取反 |
- - | 前置或后置- - |
++ | 前置或后置++ |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制转换类型 |
c语言中,0为假,非0为真。
if (flag == 0)
和if (!flag)
意思是相同的。if (flag != 0)
和if (flag)
的意思是相同的。 – (负值) 和 +(正值)
–(负值)可以得到一个变量的负值,例如:int a = 3;int b = -a;
,这样b就被赋值为-3 。
&(取地址)和 *(间接访问操作符)
这两个操作符主要应用于指针。
#include
int main()
{
int a = 10;
//pa是指针变量
//&-取地址操作符-取出a的地址
int* pa = &a;//这里的 * 是指针类型的一部分,不是操作符
//*—解引用操作符(间接访问操作符)-通过pa中存放的地址,找到指向的空间(内容)
*pa = 20;//找到空间
int c = *pa;//找到内容
return 0;
}
sizeof(int)
或者sizeof(a)
,对于这种里面是类型的,相当于计算该类型创建的变量的大小,注意:计算变量的时候,()可以去掉,例如sizeof a
,这也正好说明了sizeof是一个操作符,而不是函数(函数的()是不可以省略的),但是括号里面是类型的话,是不可以的省略的sizeof int;//错误
。#include
int main()
{
int a = -1;//a的补码为:11111111 11111111 11111111 11111111
int b = ~a;//b的补码为:00000000 00000000 00000000 00000000
return 0;
}
一些应用:
15 的补码是00000000 00000000 00000000 00001111 , 如何让倒数第5个二进制位变成1 ?变化之后又如何变回原来的值?
#include
int main()
{
int a = 15;
//00000000 00000000 00000000 00001111
//与00000000 00000000 00000000 00010000 按位或 就可以完成第一个问题
a |= (1 << 4);
printf("%d\n", a);//31
//00000000 00000000 00000000 00011111
//与11111111 11111111 11111111 11101111 按位与 就可以完成第二个问题
a&= ~(1 << 4);
printf("%d\n", a);//15
return 0;
}
实现多组输入
scanf 读取失败返回的是 EOF(end of file) ,本质是 -1 。又因为~- 1
得到的值是 0,然后将这个特点写在while循环中,就可以实现。
#include
int main()
{
int n = 0;
//假设读取失败,就会返回EOF(-1),~ -1 就是0,就会停止循环。
while (~scanf("%d", &n))
{
//一系列操作
}
return 0;
}
后置++,是先使用,后++,前置++,是先++,后使用 。后置++是在表达式结束后才++,前置++是即刻生效的,遇到就得先++。
例如:int a = 0;int b = a++;
相当于,b = a, a = a + 1,int a = 0;int b = ++a
相当于,a = a + 1, b = a 。-- 的逻辑也是这样的。
例如:int a = (int)2.5;//结果为2
这里把 2.5 (double 类型) 强制转化为 int类型。需要注意,强制转换可能导致数据丢失,所以最好类型匹配。
> >= < <= != ==
这些是用来判断大小关系的。
其中 >= 为 大于等于 ; <= 为 小于等于; == 用于判断相等 ;!= 用于判断不等。这些判断也只能应用于适合的类型上。
&& ||
&& 为 逻辑与(并且) ,|| 为逻辑或(或者)。
注意:
逻辑操作符 && 和 || 包括 ! ,只关注真假,假用0表示,真用1表示。int a = 3 && 5;
a 的结果为 1 。
特点:
- 对于&&,左边为假,右边就不计算。
- 对于| | ,左边为真,右边就不计算。
例子:
#include
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//1 2 3 4
return 0;
}
#include
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++||++b||d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//1 3 3 4
return 0;
}
exp1 ? exp2 :exp3
c语言中唯一的一个三目操作符(有三个操作数)。
例如:比较一个大小
int max = (firstNum > secondNum ? firstNum : secondNum);
这个操作符的效果类似于 if else 的效果,不建议将这个操作符运用的很复杂,这样会影响可读性。
exp1, exp2, exp3, … expN
其实就是用逗号隔开的表达式。
从左向右依次计算,逗号表达式的结果就是最后一个表达式的结果。
例:
// 例1:
int a = 1;
int b = 2;
int c = (a+=1,b+=2,b-a); //2
//例2:
int a = 2;
int b = 0;
if (a--,b += 2, a > 0)//逗号表达式
{
//处理
}
一些代码也可以改写为逗号表达式。
将:
//这个代码有点冗余
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
a = get_val();
count_val(a);
}
改写为:
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}
不仅代码的逻辑一样,而且还处理了代码冗余的问题。
[ ] () . ->
操作数:一个数组名 + 一个索引值
例如,arr[5]
两个操作数分别为 arr 和 5 。
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数(对于函数调用操作符来说,最少有1个操作数 )。
例如:ADD(3,2)
操作数有3个,一个是ADD,一个是参数 3 ,一个是参数 2 。test()
操作数有1个,只有 test。
两个操作数的使用方式 :
. 结构体变量.成员名
-> 结构体指针->成员名
例如:
struct Person
{
char name[20];
int age;
};
int main()
{
struct Person s = { "张三",20 };
printf("姓名:%s,年龄:%d\n", s.name, s.age);// 用 . 访问
struct Person* p = &s;
printf("姓名:%s,年龄:%d\n", p->name, p->age); //用-> 访问
return 0;
}
文章到这里就结束了,如果对你有帮助,你的点赞将会是我的最大动力,如果大家有什么问题或者不同的见解,欢迎大家的留言~