「地表最强」C语言(八)操作符

环境:CLion2021.3;64位macOS Big Sur


「地表最强」C语言(一)基本数据类型
「地表最强」C语言(二)变量和常量
「地表最强」C语言(三)字符串+转义字符+注释
「地表最强」C语言(四)分支语句
「地表最强」C语言(五)循环语句
「地表最强」C语言(六)函数
「地表最强」C语言(七)数组
「地表最强」C语言(八)操作符
「地表最强」C语言(九)关键字
「地表最强」C语言(十)#define定义常量和宏
「地表最强」C语言(十一)指针
「地表最强」C语言(十二)结构体、枚举和联合体
「地表最强」C语言(十三)动态内存管理,含柔性数组
「地表最强」C语言(十四)文件
「地表最强」C语言(十五)程序的环境和预处理
「地表最强」C语言(十六)一些自定义函数和宏
「地表最强」C语言(十七)阅读程序

八、操作符

  1. 算术操作符:+ 、-、*、/、%
  2. 移位操作符:>>、<<
  3. 位操作符:按位与&、按位或|、按位异或^、按位取反~
  4. 赋值操作符:=、+=、*=、%=、<<= 、>>=、&=、|=、^= 等
  5. 单目操作符:+、-、++、–、!、强制类型转换、&、sizeof、*(解引用)、~(按位取反)
  6. 关系操作符:==、!=、>=、<= 等。
  7. 逻辑操作符:&&、||
  8. 条件操作符:exp1 ? exp2 : exp3;
  9. 逗号表达式:exp1,exp2,exp3,…,expn;
  10. 下标引用、函数调用和结构成员操作符:[]:数组访问元素 、():实际上,函数名后边的圆括号就是操作符。

几个易错操作符:

8.1 算术操作符

注意两点即可:
(1)/,操作数至少有一个为浮点数时,结果才是小数。否则会取商得到整数,即使以%f打印。
(2)%,两个操作数必须是整数。

8.2 移位操作符

移位操作符移动的是二进制位。
整数在内存中以补码的形式存储。
正整数:原码 = 反码 = 补码
负整数:
    原码:根据数值写出来的二进制序列。
    反码:原码的符号位不变,其余位按位取反。
    补码:反码+1

	int a = 10;
	int b = a << 2;//b=40
	int c = a >> 2;//c=2

位操作符操作的是二进制位,先将用int存储的10用二进制表示,共4*8=32bit:

00000000 00000000 00000000 00001010
左移两位:
00000000 00000000 00000000 00101000 左边丢弃,右边补0
b = 1*2^5 + 1*2^3 = 40
右移两位:
00000000 00000000 00000000 00000010 右边丢弃,左边补原数字符号位
c = 2

左移:左边丢弃,右边补0
右移:
    1.算术右移:右边丢弃,左边补符号位,运算时一般是算术右移
    2.逻辑右移:右边丢弃,左边补0
对于正整数,左移n位相当于乘2^n;右移n位相当于除以2^n

8.3 位操作符

位操作符的操作数必须是整数!

8.3.1 异或

交换两个数字,不能定义新的变量:

	int a = 3;
	int b = 5;
	a = a + b;	//数值太大可能会溢出
	b = a - b;
	a = a - b;

上述代码的问题:a,b均在int范围之内,但是和是可以超过int的范围的,就会溢出。
采用异或:

	a = a ^ b;
	b = a ^ b;//b = a ^ b ^ b = a ^ 0 = a
	a = a ^ b;//a = a ^ b ^ a ^ b ^ b = a ^ a ^ b ^ b ^ b = 0 ^ 0 ^ b = b
	//	0 ^ a = a		a ^ a = 0

8.3.2 按位反操作符

按位取反包括符号位

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

结果为:-1。
整数在内存中是以补码的形式存储,而打印出来的是原码,因此需要将补码转化为原码。
正数的原码、反码、补码相同;负数将其原码除符号位以外按位取反得到其反码,反码+1得到补码。

a:00000000 00000000 00000000 00000000
~a:11111111 11111111 11111111 11111111
此时的~a为补码形式,将其-1得到其反码:
[~a]反:11111111 11111111 11111111 11111110
符号位为1,负数,除符号位将其按位取反得到原码:
[~a]原:10000000 00000000 00000000 00000001 即 -1

8.3.3 小练习

求一个整数在内存中二进制位为1的个数:

int	numOfBin(int n)
{
	int count = 0;
	int flag = 1;
	for (int i = 0; i < 32; i++)
	{
		flag = (n >> i) & 1;
		if (1 == flag)
			count++;
	}
	return count;
}

8.4 赋值操作符

I have nothing to say.

8.5 单目操作符

&:取地址。
*:解引用/间接访问,通过地址找到其所指向的对象。

sizeof:不是函数,括号内为变量时,可省略括号,为数据类型时不可省略。

	int a = 10;
	int arr[10] = { 0 };
	printf("%d\n", sizeof a);
	printf("%d\n", sizeof arr);//sizeof计算变量的时候可以省略括号,但计算关键字的时候不可以省略。
	printf("%d\n", sizeof(int [10]));//int [10]为数组的类型(即数组类型为去掉数组名剩下的部分)
	short s = 5;
	int a = 10;
	printf("%d\n", sizeof(s = a + 2));//sizeof括号中放的表达式不参与运算!sizeof是在编译期间处理的,而表达式是在运行期间处理的
	//sizeof编译过后,此段代码就已经无效了,编译只是看一下括号内的最终类型,而并没有实际运算。
	printf("%d\n", s);

8.6 关系运算符

就是比较大小。
注意,有时比较相等不能使用==,例如比较字符串。

8.7 逻辑操作符

&&(若左边为假,右边不参与计算)
||(若左边为真,右边不参与计算)

	int i = 0, a = 0, b = 2, c = 3, d = 4;
	//i = a++ && ++b && d++;//1 2 3 4
	i = a++ || ++b || d++;//1 3 3 4
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);

注意区分逻辑操作符和位操作符:

  • &&是逻辑运算,其两个操作数表示“真(非0)”或“假(0)”,将真假作与运算;
  • &是位运算,将两个操作数按位作与运算。
	if (1 && 2) {
		printf("true");
	}
	else {
		printf("false");
	}

1和2均为真,结果为真,即ture:

	if (1 & 2) {
		printf("true");
	}
	else {
		printf("false");
	}

1:00000000 00000000 00000000 00000001
2:00000000 00000000 00000000 00000010
1&2:00000000 00000000 00000000 00000000 即 0 ,为假
在这里插入图片描述

8.8 条件操作符

也叫三目操作符

exp1 ? exp2 : exp3;//若表达式1的值为真,那么整个表达式的值就是表达式2的值,否则就是表达式3的值。

8.9 逗号表达式

从左到右依次计算,整个表达式的值为最后一个表达式的结果。

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

(1)下标引用[]:例如数组取元素arr[4]; 操作数:arr、4
(2)函数调用():sum(a,b); 操作数:sum、a、b
(3)结构成员访问操作符 ->和.

struct Book
{
	char name[20];
	char id[20];
	double price;
};

	//	.	通过结构体变量名访问结构体成员变量:结构体变量名.成员名
	struct Book book = { "理想国","A21546",80.25 };
	struct Book* pb = &book;
	printf("书名:%s\n", book.name);
	printf("书号:%s\n", book.id);
	printf("价格:%lf\n", book.price);

	printf("书名:%s\n", (*pb).name);
	printf("书号:%s\n", (*pb).id);
	printf("价格:%lf\n", (*pb).price);

	//	->	通过指针访问结构体的成员变量:结构体指针->成员名
	printf("书名:%s\n", pb->name);
	printf("书号:%s\n", pb->id);
	printf("价格:%lf\n", pb->price);

8.11 表达式求值

表达式求值的顺序一部分是有操作符的优先级和结合性决定,有些表达式的操作数在求值的过程中可能需要转换为其它类型。

8.11.1 隐式类型转换

1.整型提升
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU(general - purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

	char a = 3;//char有符号
	//00000011 - a 符号位为0 - 没有达到int型,需要高位补0提升为整型
	//00000000 00000000 00000000 00000011
	char b = 127;
	//01111111 - b 符号位为0 - 没有达到int型,需要高位补0提升为整型
	//00000000 00000000 00000000 01111111
	char c = a + b;
	//a和b都没有达到int大小,这时会发生整型提升,整型提升是按照变量的数据类型的符号位来提升的(符号位是什么就全补什么)
	//若数据类型为无符号,高位补0.
	//00000000 00000000 00000000 00000011 - a
	//00000000 00000000 00000000 01111111 - b
	//00000000 00000000 00000000 10000010 - a+b
	//10000010 - c	char只有8个比特位,因此截断低8位,但要打印%d,因此也需要整型提升,此时符号位为1
	//11111111 11111111 11111111 10000010	补
	//11111111 11111111 11111111 10000001	反
	//10000000 00000000 00000000 01111110	原 = -126
	printf("%d\n", c);//-126
	char a = 0xb6;
	//10110110 - a
	//11111111 11111111 11111111 10110110 - 整型提升后的a
	//00000000 00000000 00000000 10110110 - 0xb6 != a
	short b = 0xb600;
	//10110110 00000000	- b
	//11111111 11111111 10110110 00000000 - 整型提升后的b
	//00000000 00000000 10110110 00000000 - 0xb600 != b
	int c = 0xb6000000;
	//10110110 00000000 00000000 00000000 - c
	//10110110 00000000 00000000 00000000 - 0xb6000000 = c
	if (a == 0xb6)//假
		printf("a");
	if (b == 0xb600)//假
		printf("b");
	if (c == 0xb6000000)//真
		printf("c");
	char c = 1;
	printf("%u\n", sizeof(c));//sizeof返回无符号整数,%u用于打印无符号整数
	printf("%u\n", sizeof(+c));//+c虽然未参与运算(sizeof虽然是编译时,括号内表达式不参与运算,但是可以推导出其类型属性),但计算机知道其类型属性,发生了整型提升
	printf("%u\n", sizeof(-c));//-c虽然未参与运算,发生了整型提升
	//任何一个表达式都有两种属性:1.值属性(就是最后的值)	2.类型属性,计算机可以推导出来

2.算术转换
下列操作符的操作数若是不同的类型,需要向上转换,否则无法计算。

int - unsigned int - long int - unsigned long int - float - double - long double 

8.11.2 操作符属性

1.优先级 2.结合性(L-R || R-L 从左到右或从右到左) 3.是否控制求值顺序
C语言操作符优先级等属性点击下边链接即可下载使用:
C语言操作符的优先级,结合性,用法示例,结果类型等属性.pdf

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值