C语言基础:运算符

运算符

算数运算符

+:正值运算符(一元运算符)
-:负值运算符(一元运算符)
+:加法运算符(二元运算符)
-:减法运算符(二元运算符)
*:乘法运算符
/:除法运算符
%:余值运算符

+ & -

**+和-既可以作为一元运算符,也可以作为二元运算符。**所谓“一元运算符”,指的是只需要一个运算数就可以执行。一元运算符-用来改变一个值的正负号。

int x = -1;

一元运算符+对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。

int x = -1;
int y = +x; // -1

二元运算符+和-用来完成加法和减法。

int x = 1 + 1;
int y = 1 - 1;

ps:如果想在prinf()中打印出+

#include <stdio.h>

int main() {
    int value = 1;
    printf("%+d\n", value);  // +1

    return 0;
}

*

运算符*用来完成乘法。

int num = 2;
printf("%i\n", num * num); // 输出 4

/

运算符/用来完成除法。注意,两个整数相除,得到还是一个整数。

#include <stdio.h>

int main() {
    float a = 6 / 4;
    
    printf("%f\n", a); // 1.0000

    return 0;
}

Q:为什么是1.0000?
A:
尽管变量a的类型是float(浮点数),但是6 / 4得到的结果是1.0,而不是1.5。在C语言中,当你进行两个整数之间的除法运算时(如 6 / 4),结果会被截断为整数。整数除法是整除,只会返回整数部分,丢弃小数部分。在没有指定浮点数的情况下,x 被赋值为 1,而不是 1.5。

float a = 6.0 / 4;
printf("%f\n", a);// 1.500000

另一个例子

int score = 5;
score = (score / 20) * 100;

上述代码,你可能觉得经过运算,score会等于25,但是实际上score等于0。这是因为score / 20是整除,会得到一个整数值0,所以乘以100后得到的也是0。
正确写法

score = (score / 20.0) * 100;

%

运算符%表示取余运算,即返回两个整数相除的余值。这个运算符只能用于整数类型(如 int、long、short 等),不能直接用于浮点数(如 float 或 double)。 这个是C语言标准,如果能够编译成功,可能是进行了某种隐式的类型转换,并且这个转换并非按照C语言标准的规定进行。

int a = 6 % 4; // 2

负数取余的规则是,结果的正负号由第一个运算数的正负号决定

11 % -5 // 1
-11 % -5 // -1
-11 % 5 // -1

赋值运算的简写形式

+= 加法赋值运算符
-= 减法赋值运算符 
*= 乘法赋值运算符 
/= 除法赋值运算符
%= 求余(模)赋值运算符
i += 3;  // 等同于 i = i + 3
i -= 3;  // 等同于 i = i - 3
i *= 3;  // 等同于 i = i * 3
i /= 3;  // 等同于 i = i / 3
i %= 3;  // 等同于 i = i % 3

每一种复合赋值运算符都会先执行右侧的运算,再将结果赋给左侧的变量。

自增运算符,自减运算符

C 语言提供两个运算符,对变量自身进行+ 1和- 1的操作。

++ 自增运算符

#include <stdio.h>

int main() {
     int a = 1;
    
    printf("%i\n", a++); 

    return 0;
}

Q:为什么打印是1,不是2?
A:
因为在C语言中,a++ 是后缀自增运算符,它的工作方式是:

  1. 首先,printf(“%i\n”, a++); 表达式执行时,会先取出 a 当前的值(此时 a 的值为1)作为参数传给 printf 函数进行打印。
  2. 然后,a++ 的自增操作才会执行,即将 a 的值加1,变为2。

所以,虽然 a 最终会增加到2,但在 printf 函数打印的时候,它使用的是自增操作前的值1进行输出。因此,打印的结果是1。
++和–,两个运算符放在变量的前面或后面,结果是不一样的。++var和–var是先执行自增或自减操作,再返回操作后var的值;var++和var–则是先返回操作前var的值,再执行自增或自减操作。

printf("%i\n", ++a);  // 2

– 自减运算符

与++类似

关系运算符

C 语言用于比较的表达式,称为“关系表达式”(relational expression),里面使用的运算符就称为“关系运算符”(relational operator),主要有下面6个。

> 大于运算符
< 小于运算符
>= 大于等于运算符
<= 小于等于运算符
== 相等运算符
!= 不相等运算符
a == b;
a != b;
a < b;
a > b;
a <= b;
a >= b;

关系表达式通常返回0或1,表示真伪。C 语言中,0表示伪,所有非零值表示真。比如,20 > 12返回1,12 > 20返回0。

#include <stdio.h>

int main() {
	
  	printf("%d\n", 20 > 12);  // 1

    return 0;
}
#include <stdio.h>

int main() {
	
  	printf("%d\n", 20 < 12);  // 0

    return 0;
}

注意:i < j < k,多个关系运算符不宜连用。
关系表达式常用于if或while结构。(后面讲)

逻辑运算符

逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,主要有下面三个运算符。

!:否(取反)运算符(改变单个表达式的真伪)。
&&:与运算符(两侧的表达式都为真,则为真,否则为伪)。
||:或运算符(两侧至少有一个表达式为真,则为真,否则为伪)。

主要讲一下&&和||

&&运算符

  • 如果左侧表达式的值为假(0),则无论右侧表达式的值是什么,整个表达式的结果都是假。因此,编译器在这种情况下不会评估右侧表达式,这就是所谓的“短路求值”或“短路逻辑”。
  • 示例:if (expression1 && expression2),如果 expression1 的值为假,则不会执行 expression2。

|| 运算符

  • 如果左侧表达式的值为真(非零),则无论右侧表达式的值是什么,整个表达式的结果都是真。同样地,编译器在这种情况下不会评估右侧表达式。
  • 示例:if (expression1 || expression2),如果 expression1 的值为真,则不会执行 expression2。

位运算符

C 语言提供一些位运算符,用来操作二进制位(bit)。

二进制位(Bit),是计算机中表示信息的最小单位,通常简称为“位”。在二进制系统中,每个位只能存储两种可能的状态,通常用0和1来表示。这是因为二进制是基于基数2的计数体系,只有两个数字——0和1。

取反运算符~

取反运算符~是一个一元运算符,用来将每一个二进制位变成相反值,即0变成1,1变成0。
Q:二进制的一些理解
A:
在计算机科学中,数字通常可以以不同的进制来表示。我们日常生活中常用的十进制(decimal)系统是以10为基础的,每一位数的权重依次是10的0次方、10的1次方、10的2次方等。
二进制(binary)则是以2为基础的进位计数制,只有两个符号:0和1。每一位的权重依次是2的0次方、2的1次方、2的2次方等。
比如int a = 0b0101;,这里的0b是一个前缀,用来告诉编译器后面的数值是用二进制表示的。具体到这个数值:

  • 第一位(最右边)是2的0次方,等于1(2^0);
  • 第二位是2的1次方,等于2(2^1);
  • 第三位是2的2次方,等于4(2^2);
  • 第四位是2的3次方,等于8(2^3);

Q:如何理解” 第一位(最右边)是2的0次方,等于1(2^0)”?
A:
本质上是根据二进制位的权重计算出该位上的数值对于整个二进制数整体数值的贡献,在二进制(或者任何进制)数中,每一位都有一个权重,这个权重是由基数(这里是2)的幂次决定的,幂次则表示该位在数中的位置(从右往左数,位置编号从0开始计数)
基于以上理解:
因为它是第0位,所以的幂次是0,在二进制中,这一位的数值只有两种可能,0或1。如果这一位是1,那么它对整个二进制数的贡献就是1乘以该位的权重,也就是1 * 2^0。任何数的0次方都是1,所以这里1 * 2^0 = 1。
对于二进制数0b101,最右边的位(第0位)是1,其权重是2^0=1,因此这一位的数值贡献是1。将一个二进制数转换为十进制数时,我们需要将每一位上的数字乘以其对应的权重,就是0 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 0 + 4 + 0 + 1 = 5,这样就把二进制数转换成了对应的十进制数。

权重是用来衡量每个位上的数字对整个数值大小的影响程度。

~ 10010011 // 返回 01101100

~运算符不会改变变量的值,只是返回一个新的值。

与运算符&

&将两个值的每一个二进制位进行比较,返回一个新的值。当两个二进制位都为1,就返回1,否则返回0。
以下示例:两个八位二进制数进行逐位比较,返回一个新的值。

// 返回 00010001
10010011 & 00111101

简写场景

int val = 3;
val = val & 0377;

// 简写成
val &= 0377;

与运算符&常用于检查标志位,位掩码操作。

或运算符|

或运算符|将两个值的每一个二进制位进行比较,返回一个新的值。两个二进制位只要有一个为1(包含两个都为1的情况),就返回1,否则返回0。

// 返回 10111111
10010011 | 00111101
int val = 3;
val = val | 0377;

// 简写为
val |= 0377;

逻辑或通常是双竖线 ||,而单个竖线 | 更多用于按位或运算。

异或运算符^

异或运算符^将两个值的每一个二进制位进行比较,返回一个新的值。两个二进制位有且仅有一个为1,就返回1,否则返回0。

// 返回 10101110
10010011 ^ 00111101

异或运算符^可以与赋值运算符=结合,简写成 ^=。

int val = 3;
val = val ^ 0377;

// 简写为
val ^= 0377;

左移运算符<<

左移运算符<<将左侧运算数的每一位,向左移动指定的位数,尾部空出来的位置使用0填充。

// 1000101000
10001010 << 2

上面示例中,10001010的每一个二进制位,都向左侧移动了两位。
左移运算符相当于将运算数乘以2的指定次方,比如左移2位相当于乘以4(2的2次方)。
Q:如何理解?
A:

int x = 4; // x 的二进制是 00000000000000000000000000000100 (32位系统中,未显示的高位均为0)

x = x << 2; // 把 x 的二进制值向左移动2位

// 移动后,x 的值变成了:
// 00000000000000000000000000100000

原本的 100 左移两位后变成了 100000,这个新的二进制数转换成十进制就是 16(因为 2^4 = 16)。
因此,通过 x << 2 这个操作,我们实际上是将 x 的值从 4 改为了 16,这正好验证了左移2位相当于乘以2的2次方(也就是乘以4)的规则。

左移运算符<<可以与赋值运算符=结合,简写成<<=。

int val = 1;
val = val << 2;

// 简写为
val <<= 2;

右移运算符>>

右移运算符>>将左侧运算数的每一位,向右移动指定的位数,尾部无法容纳的值将丢弃,头部空出来的位置使用0填充。

// 返回 00100010
10001010 >> 2

上面示例中,10001010的每一个二进制位,都向右移动两位。最低的两位10被丢弃,头部多出来的两位补0,所以最后得到00100010。
注意,右移运算符最好只用于无符号整数,不要用于负数。因为不同系统对于右移后如何处理负数的符号位,有不同的做法,可能会得到不一样的结果。
右移运算符相当于将运算数除以2的指定次方,比如右移2位就相当于除以4(2的2次方)。
右移运算符>>可以与赋值运算符=结合,简写成>>=。

int val = 1;
val = val >> 2;

// 简写为
val >>= 2;

逗号运算符

逗号运算符用于将多个表达式写在一起,从左到右依次运行每个表达式。

x = 10, y = 20;

上面示例中,有两个表达式(x = 10和y = 20),逗号使得它们可以放在同一条语句里面。
逗号运算符返回最后一个表达式的值,作为整个语句的值。

int a = 5, b = 10;
int c;

c = (a++, b++); // 在这里,逗号运算符会先计算左侧的 a++(将a加1,但返回原始值5),然后计算右侧的 b++(将b加1,但返回原始值10)。
// 执行后,a的值变为6,b的值变为11,而c的值将是10(这是最后一个表达式b++执行后的原始值)。

a = 5;
b = 10;
c = a++, b++; // 这是一个复合语句,逗号运算符依然从左到右执行,但由于逗号在这里不是在赋值操作符右侧,所以c的值仍然是5(a++之前的值),而a和b的值分别增加到6和11。

运算优先级

优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不一样的。

1. 圆括号 ( ) 和其它括号运算符
2. 一元运算符(如 ++、--、-(负号)、!(逻辑非)、~(按位取反)、*(指针解引用)、&(取地址)、sizeof)
3. 算术运算符(*(乘法)、/(除法)、%(模运算))
4 .加法和减法运算符 (+、-)
5. 移位运算符 (<<、>>)
6. 关系运算符 (<、>、<=、>=、==、!=)
7. 位逻辑运算符 (&、^、|)
8. 逻辑与运算符 (&&)
9. 逻辑或运算符 (||)
10. 条件运算符 (?:)
11. 赋值运算符 (=, +=, -= 等)
12. 逗号运算符 (,)
  • 26
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值