C语言一操作符详解(各操作符介绍,表达式求值)

Posted SuchABigBug

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言一操作符详解(各操作符介绍,表达式求值)相关的知识,希望对你有一定的参考价值。


博观而约取,厚积而薄发。——《杂说送张琥》



一、算术操作符

+	-	 *	 /	 %

注意事项:

除了此符号 % 外,其他可用于整数和浮点数
% 操作符的两个操作数必须为整数。返回的是整除之后的余数。
对于操作符 / 如果两个操作数都为整数,执行整数除法。只要有符点数那么执行的就是符点数除法

这里的加和减没什么好说的但需要注意一下int的范围为-231 ~ 231-1,
也就是[-2147483648, 2147483647];

举例论证:


int a = 2147483647;
int b = 1;
printf("a + b = %d \\n", a + b); //a + b = -2147483648

int a1 = -2147483648;
int b1 = 1;
printf("a1 - b = %d \\n", a1 - b1 ); //a1 - b = 2147483647

对于除法如果用int进行接收那么即使有小数也会转换成整数,此时我们需要用double或者float类型进行接收,值也需要是浮点数

double a = 10.0;
double b = 3.0;
printf("a / b = %f\\n", a / b); // a / b = 3.333333

二、移位操作符

<< 左移操作符 
>> 右移操作符

1. 左移操作符规则

在这里插入图片描述
上述num向左移动一个位,最左边丢弃一个0, 最右边补一个0

2. 右移操作符规则

I、逻辑右移

逻辑移位,左边补0,右边丢弃

II、算术右移

算术移位,左边用用原该值的符号位填充,右边丢弃

拿-1举例子:

-1存放在内存中是二进制的补码

整数的二进制表示形式有三种
原码:直接根据数值写出的二进制序列
反码:原码的符号位不变,其他位按位取反就是反码
补码:反码+1

-1的原反补:
原码:1000000 00000000 00000000 00000001
反码:1111111 11111111 11111111 11111110
补码:1111111 11111111 11111111 11111111

如果是逻辑右移,那么按照补码来看,最右边的1丢弃,最左边补0,就变成了一个非常大的数。
如果是算术右移,那么右边丢弃1,左边补1

三、位操作符

按位与 ---> &  		举例:0 & 0 = 0,  1 & 0 = 0, 1 & 1 = 1 
按位或 ---> |  		举例: 0 | 0 = 0, 0 | 1 = 1, 1 | 1 = 1 
按位异或 ---> ^ 		举例: 0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 1 = 0 

一道面试题:不能创建临时变量(temp),如何实现两个数的转换?

int a = 10;
int b = 20;
a = a + b;
b = a - b;
a = a - b;
printf("a is : %d \\n", a); // a is 20
printf("b is : %d \\n", b); // b is 10
//======================================
//========== 上述代码相加可能溢出 =========
//======================================
int x = 3;
int y = 5;
x = x ^ y;
y = x ^ y;
x = x ^ y;
printf("x is : %d \\n", x); // x is 5
printf("y is : %d \\n", y); // y is 3

有的人可能会想到两个数相加,但这不是最好的答案,因为我们在算术操作符中提到过int最大为231-1,考虑溢出,我们用异或来进行计算,我们来具体分析一下两个数是如何进行转换的

整数3用二进制表示为:011
整数5用二进制表示为:101

第一次异或: 011 ^ 101 = 110  // 6
第二次异或: 110 ^ 011 = 101  // y = 5
第三次次异或: 110 ^ 101 = 011  // x = 3

其实异或的本质就是相同为0,相异为1,就像上述的交换
实际上就是 x ^ y ^ y,那么第二次异或y和y为0,y得到x的值
相同道理,第三次异或x ^ y ^ x,那么只剩下y,x得到y的值

四、赋值操作符

简单来说,就是把右边的值赋值到左边放进变量进行存储

五、单目操作符

逻辑反操作               !
负值                    -
正值                    +
操作符的类型长度          sizeof
对一个数的二进制按位取反    ~
分为前值、后置            --
分为前值、后置            ++
解引用操作符              *
取地址                   &
及强制类型转换

我们重点看一下取地址符和指针解引用

int a = 5;
printf("%p \\n", &a); 

int* pa = &a;
printf("*pa : %d \\n", *pa);

*pa = 10;
printf("%d \\n", a);

这里的数字5需要存储,a变量在内存中需要开辟空间,开辟一个int型4个字节,而存储的空间则会化分为一个个小的内存单元,每个内存单元都有一个编号,而编号就是内存单元的地址,那么&a就是取a的地址。int* pa ,这里pa是用来存放地址的,pa就是一个指针变量,通过pa里面存放的地址,我们用*pa解引用来获取他所指向对象的值。通过 *pa也可以间接改变所指向a的内存空间的值。

sizeof和数组:
在这里插入图片描述
在这里插入图片描述
可以看到打印的结果,在传参之前,数组打印出来的sizeof如预期的显示所占空间的大小,但传给函数后进行sizeof打印,结果可能是4,也可能是8。因为数组中传过去的实际上是首元素的地址。那么指向这块内存的就是一个指针,4或者8取决于电脑是32位还是64位的。

六、关系操作符

> , >=
< , <=
!=
== 判断两个数是否相等
= 赋值

七、逻辑操作符

逻辑与 &&
逻辑或 ||

对于&&,如果逻辑与左边的条件为假的话,后面就继续判断了

int a = 0;
int b = 2;
int c = 3;
int i = 0;

i = a++ && ++b && ++c;

printf("a = %d\\n b = %d\\n c = %d\\n", a, b, c); // a = 1, b = 2, c = 3

上述的a为0,先使用后++,先判断a为假,后面就不做判断了,a自增后结束,因此结果为1 2 3

对于||,如果逻辑或左边的条件为真的话,后面就不用算了

int a = 0;
int b = 2;
int c = 3;
int i = 0;

i = a++ || ++b || ++c;

printf("a = %d\\n b = %d\\n c = %d\\n", a, b, c); // a = 1, b = 3, c = 3

结果为a = 1, b = 3, c = 3。可以看到a为后置++,先使用a,a为0,条件为假,a自增,继续向后判断,前值b加加后为3,条件为真,后面就不做判断了,因此结果为 1 3 3

八、条件操作符

三目操作符: a > b ? 1 : 0
这里的意思是a大于b吗?true返回1,否则为0

九、逗号表达式

逗号表达式应用场景:要从左向右依次计算,但是整个表达式的结果传给变量以最后一个为准。

int a = 10;
int b = 20;
int c = 30;

int d = (c +=20, a = c + 50, b /= 5, c+=a); 
printf("d is %d \\n", d); //d is 150

十、下标引用、函数调用和结构成员

  1. [ ]下标引用操作符 ,操作数:一个数组名+一个索引值
  2. 函数调用操作符( ) ,一个函数callFunc( )
  3. 结构成员访问操作符分两种 . 和 ->
struct student{
    char* name;
    char gender;
    int age;
};

int main(int argc, const char * argv[]) {

    struct student henry = {"Leetcode King", 'M', 18};
    printf("Student Name : %s \\n", henry.name);
    printf("Student Gender : %c \\n", henry.gender);
    printf("Student Age : %d \\n", henry.age);

    struct student* ptr = &henry;

    // printf("Student Name : %s \\n", (*ptr).name);
    // printf("Student Gender : %c \\n", (*ptr).gender);
    // printf("Student Age : %d \\n", (*ptr).age);

    //下面为简化后
    printf("Student Name : %s \\n", ptr->name);
    printf("Student Gender : %c \\n", ptr->gender);
    printf("Student Age : %d \\n", ptr->age);
	
	return 0;
}

我们可以用.进行成员名访问,也可以创建结构体指针进行成员名的访问(*ptr),为了更加简单明了我们可以用->进行成员访问。


如果文章对你有帮助的话,给博主一键三连哦 Thank You~ 😃

以上是关于C语言一操作符详解(各操作符介绍,表达式求值)的主要内容,如果未能解决你的问题,请参考以下文章

刁肥宅详解中缀表达式求值问题:C++实现顺序/链栈解决

C语言学习笔记详解操作符

C语言编程-逆波兰表达式求值

操作符中表达式求值(隐式类型转换详解)以及操作符属性

C语言关于表达式求值

《C程序设计语言》笔记 参考手册3