C语言-操作符详解(5)

目录

1. 操作符分类:

思维导图:

2. 算术操作符

3. 移位操作符

3.1 左移操作符

3.2 右移操作符

4. 位操作符

4.1 &

4.2 |

4.3 ^

5. 赋值操作符

6. 单目操作符

6.1 单目操作符介绍

6.2 sizeof 和数组

7. 关系操作符 

8. 逻辑操作符

9. 条件操作符

10. 逗号表达式

11. 结构成员

12. 表达式求值

12.1 隐式类型转换

12.2 算术转换

12.3 操作符的属性

写在最后


1. 操作符分类:

思维导图:

C语言-操作符详解(5)_第1张图片

2. 算术操作符

+     -     *     /     %
#include 
int main()
{
	int a = 10 / 3;//除法操作符,两边都是整数就执行整数除法
	printf("%d\n", a);
	double b = 10.0 / 3;//两边只要有一个是浮点数就能计算出小数
	printf("%lf\n", b);
	printf("%.1lf\n", b);//在lf前面加.1能保留小数点后一位,.几就保留几位
	return 0;
}

输出结果:

输出:
3
3.333333
3.3

#include 
int main()
{
	int ret = 10 % 3;//取模,操作符两边必须是整数
	printf("%d\n,ret");
	return 0;
}

输出结果:

输出:1

3. 移位操作符

在讲移位前,需要知道一些基本的概念:

二进制:

二进制整数有三种表示形式:

1. 原码

2. 反码

3. 补码

而在内存中存储的是:二进制的补码

所以参数在移位时是二进的补码。

例:

int main()
{
	//整形类型占四个字节(32个比特位),二进制的表现形式:

	int a = 10;
	//00000000000000000000000000001010 - 原码
	//00000000000000000000000000001010 - 反码
	//00000000000000000000000000001010 - 补码

	int a = -10;
	//10000000000000000000000000001010 - 原码
	//11111111111111111111111111110101 - 反码
	//11111111111111111111111111110110 - 补码

	return 0;
}

通过观察发现正数原码、反码、补码相同,

负数反码是:原码符号位不变,其它位按位取反,补码是:反码+1。

3.1 左移操作符

例:

#include 
int main()
{
	//左移操作符:<<
	//规则:左边抛弃,右边补零

	int a = 10;
	//a:
	//00000000000000000000000000001010 - 补码

	int b = a << 1;
	//b:
	//00000000000000000000000000010100 - 补码

	printf("%d\n", b);

	int c = -10;
	//c:
	//10000000000000000000000000001010 - 原码
 	//11111111111111111111111111110101 - 反码
	//11111111111111111111111111110110 - 补码

	int d = c << 1;
	//d:
	//11111111111111111111111111101100 - 补码
	//11111111111111111111111111101011 - 反码
	//10000000000000000000000000010100 - 原码

	printf("%d\n", d);

	return 0;
}

(注:printf 打印出来给我们看的是原码。)

输出结果:

输出:
 20
-20

总结:

左移操作也可以看成是乘二的操作。

3.2 右移操作符

例:

#include 
int main()
{
	//右移操作符:>>
	//1.算数右移:左边补符号位,右边抛弃(常用)
	//2.逻辑右移:左边补零,右边抛弃

	int a = 10;
	//a:
	//00000000000000000000000000001010 - 补码

	int b = a >> 1;
	//b:
	//00000000000000000000000000000101 - 补码

	printf("%d\n", b);

	int c = -10;
	//c:
	//10000000000000000000000000001010 - 原码
	//11111111111111111111111111110101 - 反码
	//11111111111111111111111111110110 - 补码

	int d = c >> 1;
	//d:
	//11111111111111111111111111111011 - 补码
	//11111111111111111111111111111010 - 反码
	//10000000000000000000000000000101 - 原码

	printf("%d\n", d);

	return 0;
}

输出结果:

输出:
 5
-5

注:无论是右移还是左移的位数都不能为负数。

例:

ret>>-1
这样写是错误的

4. 位操作符

&   按位与

 |     按位或 

^    按位异或

4.1 &

例:

#include 
int main()
{
	//& - 按二进制与
	//规则:有零则零
	int a = 3;
	//00000000000000000000000000000011 - 补码

	int b = -5;
	//10000000000000000000000000000101 - 原码
	//11111111111111111111111111111010 - 反码
	//11111111111111111111111111111011 - 补码

	int c = a & b;
	//00000000000000000000000000000011 - a 补码
	//11111111111111111111111111111011 - b 补码

	//00000000000000000000000000000011 - c 原码

	printf("%d\n", c);
	return 0;
}

输出结果:

输出:3

4.2 |

例:

#include 
int main()
{
	// | - 按二进制位或
	// 规则:有一则一

	int a = 3;
	//00000000000000000000000000000011 - 补码

	int b = -5;
	//10000000000000000000000000000101 - 原码
	//11111111111111111111111111111010 - 反码
	//11111111111111111111111111111011 - 补码
	
	int c = a | b;
	//00000000000000000000000000000011 - a补码
	//11111111111111111111111111111011 - b补码

	//11111111111111111111111111111011 - c补码
    //11111111111111111111111111111010 - c反码
    //10000000000000000000000000000101 - c原码
	
	printf("%d\n", c);

	return 0;
}

输出结果:

输出:-5

4.3 ^

例:

#include 
int main()
{
	// ^ - 按二进制位异或
	// 规则:相同为零,相异为一

	int a = 3;
	//00000000000000000000000000000011 - 补码

	int b = -5;
	//10000000000000000000000000000101 - 原码
	//11111111111111111111111111111010 - 反码
	//11111111111111111111111111111011 - 补码

	int c = a ^ b;
	//00000000000000000000000000000011 - a补码
	//11111111111111111111111111111011 - b补码
	
	//11111111111111111111111111111000 - c补码
	//11111111111111111111111111110111 - c反码
	//10000000000000000000000000001000 - c原码

	printf("%d\n", c);

	return 0;
}

输出结果:

输出:-8

练习:

一道编程题:

如何不创建临时变量完成两个数的交换

例:

#include 
int main()
{
	int a = 3;
	int b = 5;

	printf("%d %d\n", a, b);

	a = a ^ b;
	b = a ^ b;// b = a ^ b ^ b  // 而 a ^ b ^ b = a 所以b就赋值为a
	a = a ^ b;// a = a ^ b ^ a  // 而 a ^ b ^ a = b 所以a就赋值为b

	printf("%d %d\n", a, b);

	return 0;
}

输出结果:

输出:
3 5
5 3

用异或操作符交换两个变量的弊端:

1. 可读性差

2. 效率没有创建临时变量高

3. 异或只能用于整数变量的交换

总结:这种方法了解即可,平时使用临时变量交换两个变量的方法更好。

5. 赋值操作符

赋值操作符能给变量赋值。

例:

int main()
{
	int a = 10;
	a = 100;// = 能将a赋值成100
	return 0;
}

还有复合赋值符:

例:

+=     

-=       

*=       

/=       

%=

>>=     

<<=     

&=      

|=     

^=

这些运算符都可以写成复合的效果(规则都是一样的)

例:

int main()
{
	int a = 10;
	a += 10;//这个其实就是:a = a + 10
	printf("%d\n", a);//输出的结果就是20
	return 0;  
}

6. 单目操作符

!              逻辑反操作

-              负值

+             正值

&             取地址

sizeof      操作数的类型长度(以字节为单位)

~             对一个数的二进制按位取反

--            前置、后置--

+            前置、后置++

*             间接访问操作符(解引用操作符)

(类型)     强制类型转换

6.1 单目操作符介绍

例1:

#include 
int main()
{
    //! 逻辑反操作
	//C语言中0表示假,非零表示真
	int n = 0;
	if (n)
	{
		printf("1\n");
	}
	if (!n)//!逻辑反操作,将假变为真(也能从真变假)
	{
		printf("2\n");
	}
	return 0;
}

输出结果:

输出:2

例2:

#include 
int main()
{    // + -
	int a = -10;
	printf("%d\n", a);
	printf("%d\n", -a);
	printf("%d\n", +a);//‘+’几乎没有用处
	return 0;
}

输出结果:

输出:
-10
 10
-10

例3:


#include 
int main()
{
	// ~ 按位取反
	//00000000000000000000000000000000
	//11111111111111111111111111111111 - 补码是全1

	int a = 0;
	printf("%d\n", ~a);
	
}

输出结果:

输出:-1

例4:

#include 
int main()
{
    //++
	int a = 10;
	int b = a++;//后置++,先使用,再++
	printf("%d\n", b);
	printf("%d\n", a);

	int c = 10;
	int d = ++c;//前置++,先++,再使用
	printf("%d\n", d);
	printf("%d\n", c);
	return 0;
}

输出结果:

输出:
10
11
11
11

注:-- 的规则与 ++ 是一样的。

#include 

int main()
{
	// ++ -- 是带有副作用的
    // 会改变变量自身的值 
	
	//1
	int a = 10;
	int b = ++a;//b=11 a=11

	//2
	int a = 10;
	int b = a + 1;//b=11 a=10

	return 0;
}

例5:

#include 
int main()
{	// (类型)强制类型转换
	int a = (int)3.14;//这样编译器就不会报警告

	srand((unsigned int)time(NULL));//将类型为time_t的time转成srand需要的无符号整形

	return 0;
}

6.2 sizeof 和数组

例:

#include 
void test1(int arr[])//本质上传过来的是数组首元素的地址
{
	printf("%d\n", sizeof(arr));//地址在32位环境占4个字节
}                                //在64位的环境中占8个字节
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//我是32位的环境,所以输出4
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));//int类型占4个字节
	printf("%d\n", sizeof(ch));//char类型占1个字节
	test1(arr);
	test2(ch);
	return 0;
}

输出结果:

输出:
40
10
4
4

7. 关系操作符 

>

>=

<

<=

!=       用于测试“不相等”

==      用于测试“相等”

注:别把 “==” 和 “=” 给写错了。

8. 逻辑操作符

&&     逻辑与  ( a&&b    a,b都要满足才真)

 ||       逻辑或  (   a||b     a,b只要满足一个就真)

例:

#include 
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;

	i = a++ && ++b && d++;
	//因为a=0,++的优先级较低,所以(a++)这个表达式值是0,而&&有零那就是零了,
	//所以(++b)(d++)都不会发生
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);

	return 0;
}

输出结果:

输出:
a = 1
b = 2
c = 3
d = 4
#include 
int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;

	i = a++ || ++b || d++;
	//(a++)这个表达式为真,那||左边就无须计算了
	//所以(++b)(d++)都不计算了
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);

	return 0;
}

输出结果:

输出:
a = 2 
b = 2
c = 3
d = 4

9. 条件操作符

exp1 ? exp2 : exp3

例:

这是一个求较大值的代码:

#include 

int max(int a, int b)
{
	if (a > b)
		return a;
	else
		return b;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("%d\n", max(a, b));
	return 0;
}

输出结果:

输出:20

而用条件操作符:

#include 

int max(int a, int b)
{
	return (a > b ? a : b);//如果a>b则取a,否则取b
}

int main()
{
	int a = 10;
	int b = 20;
	printf("%d\n", max(a, b));
	return 0;
}

输出结果:

输出:20

两种写法是一模一样的。

10. 逗号表达式

逗号表达式:

1. 用逗号隔开的多个表达式。

2. 从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

例:

#include 

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
	//先算a = b + 10,算出a = 12,再算b = a + 1,算出b = 13,
	//最后结果就是最后一个表达式(b = a + 1)的值
	printf("c = %d\n", c);
	return 0;
}

输出结果:

输出:13

11. 结构成员

结构体在C语言中一般用于描述复杂对象,比如人、书,C语言中没有这样的类型,

但是结构体能让我们创建新的类型。

C语言-操作符详解(5)_第2张图片

12. 表达式求值

12.1 隐式类型转换

CPU在进行运算的时候一般使用整形int,所以在有些时候,当一个小于整形的类型进行计算时,计算机就会先进行整形提升再进行运算,这就是隐式类型转换。

(通用CPU是难以直接实现两个非整形的直接相加运算)

例:

// char short int long ...
//  1     2    4
int main()
{
	//char --> signed char
	char a = 3;
	//截断
	//00000000000000000000000000000011
	//00000011 - a
	//
	char b = 127;
	//00000000000000000000000001111111
	//01111111 - b

	char c = a + b;
	//00000011
	//01111111
	//整型提升
	//00000000000000000000000000000011 - a
	//00000000000000000000000001111111 - b
	//00000000000000000000000010000010 - a + b =c
	//截断
	//10000010 - c
	printf("%d\n", c);
	//%d 是打印十进制的整数
	//11111111111111111111111110000010 - 补码
	//11111111111111111111111110000001 - 反码
	//10000000000000000000000001111110 - 原码
	//-126
	return 0;
}

输出结果:

输出:-126

注:

char:

有符号的char的取值范围是:-128~127

无符号的char的取值范围是:0~255

12.2 算术转换

不同操作类型的数进行运算时会进行算数转换:

long double

double

float unsigned long int

long int

unsigned int

int

注:算数转换不合理会产生一些问题:

#include

int main()
{
	float f = 3.14;
	int num = f;//隐式转换,会有精度丢失
	printf("%d\n", num);
	return 0;
}

输出结果:

输出:3

12.3 操作符的属性

复杂表达式的求值有三个影响的因素:

1. 操作符的优先级

2. 操作符的结合性

3. 是否控制求值顺序

但是,我们写出的代码如果没有唯一的运算路径,就会出问题

所以,总结:平时写代码的运算路径要唯一。

写在最后

以上就是本篇文章的内容了,感谢你的阅读。

如果喜欢本文的话,欢迎点赞和评论,写下你的见解。

如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。

之后我还会输出更多高质量内容,欢迎收看。

你可能感兴趣的:(C语言学习,c语言,学习)