第四关,操作符详解

发布于:2023-01-04 ⋅ 阅读:(561) ⋅ 点赞:(0)

目录

操作符分类:

1.算术操作符

3. 移位操作符

3.1 左移操作符

3.2 右移操作符

4. 位操作符

4.1 按位与

4.2 按位或

4.3 按位异或

5. 赋值操作符

6. 单目操作符

6.1 单目操作符介绍

 对于&和*的解释:

对于sizeof的讲解:

对 ~ 的讲解

对++,--的讲解

(类型) 强制类型转换

7. 关系操作符

8. 逻辑操作符

练习题:

9. 条件操作符

10. 逗号表达式

练习:

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

1. [ ] 下标引用操作符

2. ( ) 函数调用操作符

3. 访问一个结构的成员

12. 表达式求值

12.1 隐式类型转换

整型提升的意义:

12.2 算术转换

12.3 操作符的属性


操作符分类:

算术操作符

移位操作符

位操作符

赋值操作符

单目操作符

关系操作符

逻辑操作符

条件操作符

逗号表达式

下标引用

函数调用

结构成员

1.算术操作符

+   -   *   /   %

1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

对于除数5/2=2.5来说,要想得到小数的结果,必须保证除数和被除数中至少有一个小数(浮点数)5.0/2=2.5

3. 移位操作符

<< 左移操作符

>> 右移操作符    

注:移位操作符的操作数只能是整数。

移位操作符,移动的是二进制,用二进制来表示

3.1 左移操作符

整形的2进制表示:

原码,反码,补码

正整数的源码,反码,补码相同

符号位是0,表示正数

符号位位1,表示负数

15——正数,在C语言可以存放到int类型的变量中,int类型是4个字节,32bit

00000000000000000000000000001111   ——   原码

00000000000000000000000000001111   ——   反码

00000000000000000000000000001111   ——   补码

-15

10000000000000000000000000001111   ——   原码

111111111111111111111111111111110000   ——   反码

111111111111111111111111111111110001   ——   补码(反码+1)

整数在内存中存储的是二进制的补码

int main()
{
	int a = 4;
	//00000000000000000000000000000100 - a的补码
	int b = a << 1;
	//左移后(右边丢弃,左边添0)
	//00000000000000000000000000001000 - b的补码
	printf("a=%d,b=%d", a, b);

	return 0;
}

输出结果:   a = 4, b = 8

int main()
{
	int a = -4;
	//10000000000000000000000000000100  -  a的原码
	//11111111111111111111111111111011  -  a的反码
	//11111111111111111111111111111100  -  a的补码
	int b = a << 1;
	//把a向左移动一位
	//11111111111111111111111111111000  -  b的补码
	//11111111111111111111111111110111  -  b的反码
	//10000000000000000000000000001000  -  b的原码
	printf("a=%d,b=%d", a, b);

	return 0;
}

输出结果:   a = -4, b = -8

3.2 右移操作符

1.逻辑右移

右边丢弃,左边补零

2.算术右移

右边丢弃,左边补原符号位

那种移动方法取决于编译器,大多数编译器都是算术右移

int main()
{
	int a = 4;
	//00000000000000000000000000000100 - a的补码
	int b = a >> 1;
	//右移后(左边丢弃,右边添原符号位)
	//00000000000000000000000000000010 - b的补码
	printf("a=%d,b=%d", a, b);

	return 0;
}

 输出结果:  a = 4, b = 2

int main()
{
	int a = -4;
	//10000000000000000000000000000100  -  a的原码
	//11111111111111111111111111111011  -  a的反码
	//11111111111111111111111111111100  -  a的补码
	int b = a >> 1;
	//把a向右移动一位
	//11111111111111111111111111111110  -  b的补码
	//11111111111111111111111111111101  -  b的反码
	//10000000000000000000000000000010  -  b的原码
	printf("a=%d,b=%d", a, b);

	return 0;
}

输出结果:  a = -4, b = -2

4. 位操作符

&   按位与

|     按位或

^    按位异或

注:他们的操作数必须是整数。

4.1 按位与

算法:有0出0,全1出1

int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;
	//00000000000000000000000000000011  -  a的原码
	//10000000000000000000000000000101  -  b的原码
	//11111111111111111111111111111010  -  b的反码
	//11111111111111111111111111111011  -  b的补码
	//
	// 0000000000000000000000000000011
	// 1111111111111111111111111111011
	// 0000000000000000000000000000011  -  3
	//
	printf("c=%d",c);

	return 0;
}

结果为3

4.2 按位或

算法:有1出1,全0出0

int main()
{
	int a = 3;
	int b = -5;
	int c = a | b;
	//00000000000000000000000000000011  -  a的原码
	//10000000000000000000000000000101  -  b的原码
	//11111111111111111111111111111010  -  b的反码
	//11111111111111111111111111111011  -  b的补码
	//
	// 0000000000000000000000000000011
	// 1111111111111111111111111111011
	// 1111111111111111111111111111011  -  c的反码
	// 1111111111111111111111111111010  -  c的补码
	// 1000000000000000000000000000101  -  c的原码
	//
	printf("c=%d", c);

	return 0;
}

结果为 -5

4.3 按位异或

算法:相同为0,相异为1

int main()
{
	int a = 3;
	int b = -5;
	int c = a ^ b;
	//00000000000000000000000000000011  -  a的原码
	//10000000000000000000000000000101  -  b的原码
	//11111111111111111111111111111010  -  b的反码
	//11111111111111111111111111111011  -  b的补码
	//
	// 0000000000000000000000000000011
	// 1111111111111111111111111111011
	// 1111111111111111111111111111000  -  c的反码
	// 1111111111111111111111111110111  -  c的补码
	// 1000000000000000000000000001000  -  c的原码
	//
	printf("c=%d", c);

	return 0;
}

结果为 -8

5. 赋值操作符

+=          -=          *=          /=          %=          >>=          <<=          &=          |=          ^=

这些运算符都可以写成复合的效果。

int x = 10;
x = x+10;
x += 10;
//复合赋值

6. 单目操作符

6.1 单目操作符介绍

逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换

 对于&和*的解释:

int main()
{
	int a = 10;
	char c = 0;

	int* pa = &a;
	//&取地址,*说明pa是存放地址的指针变量,int*说明a是int 类型
	*pa = 20;
	//*pa 是解引用操作符

	return 0;
}

对于sizeof的讲解:

void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//(2)8
}
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//(4)8
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));//(1)40
	printf("%d\n", sizeof(ch));//(3)10
	test1(arr);
	test2(ch);
	return 0;
}

 数组名:
 数组名是数组首元素的地址
 两个例外:
 1. sizeof(数组名),数组名表示整个数组,不是首元素的地址
 sizeof(数组名)计算的是整个数组的大小,单位是字节
 
 2. &数组名,数组名表示整个数组,不是首元素的地址
 &数组名去除的是整个数组的地址
 

对 ~ 的讲解

是对一个二进制位的取反

int main()
{
	int a = 0;
	// 0000000000000000000000000000000 - 0的原码
	// 0111111111111111111111111111111 - 0的反码
	// 1111111111111111111111111111111 - 0的补码

	printf("%d\n", ~a);
	// ~a:将a的补码转化为原码,结果为-1

	return 0;
}

对++,--的讲解

int main()
{
	int a = 4;
	int b = ++a;//前置++,先++后使用;
	//相当于a=a+1;b=a;

	int b = a++;//后置++,先使用,再++;
	//相当于b=a;a=a+1;
	
	//  --  和  ++  原理一样

}

(类型) 强制类型转换

int main()
{
	int a = (int)3.14;
	printf("%d\n", a);

	return 0;
}

把3.14强制转化为int类型,相当于丢到小数部分保留整数

7. 关系操作符

>            >=           <          <=          !=:用于测试 “不相等”          ==: 用于测试“相等”

注意:

在编程的过程中== 和=不小心写错,导致的错误。

8. 逻辑操作符

逻辑操作符有哪些:

&&        逻辑与

 ||          逻辑或

区分逻辑与和按位与

区分逻辑或和按位或

1&2----->0
1&&2---->1

1|2----->3
1||2---->1

练习题:

int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;
    //i = a++||++b||d++;
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    return 0;
}
//程序输出的结果是什么?

结果:1 3 3 4

9. 条件操作符

也称三目操作符

exp1 ? exp2 : exp3
表达式1为真,执行表达式2
表达式1为假,执行表达式3

10. 逗号表达式

exp1, exp2, exp3, …expN

逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

练习:

int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
//c是多少?

c为13 

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

1. [ ] 下标引用操作符

操作数:一个数组名+一个索引值

int arr[10];   //创建数组
arr[9] = 10;   //实用下标引用操作符。
[ ]的两个操作数是arr和9

2. ( ) 函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

int main()
{
	test2();
	//操作数:函数名
	test(3, 4);
	//操作数:函数名,3,4
	return 0;
}

3. 访问一个结构的成员

.      结构体.成员名
->    结构体指针->成员名

struct book
{
	char name[20];
	int price;
};
int main()
{
	struct book th = { "游戏设计", 36 };
	printf("%s %d", th.name, th.price);//结构体变量.结构体成员名

	struct book* ps = &th;
	printf("%s %d\n",ps->name,ps->price);//结构体指针->成员名

	return 0;
}

12. 表达式求值

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

12.1 隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升

整型提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

通用CPU是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。

 例如:

int main()
{
	char a = 5;//截断
	char b = 126;//截断
	char c = a + b;//截断
	//00000000000000000000000000000101
	// 截断如下
	//00000101 - a
	//00000000000000000000000001111110
	// 截断如下
	//01111110 - b
	//整型提升
	//00000000000000000000000000000101 - a
	//00000000000000000000000001111110 - b
	//00000000000000000000000010000011 - 相加
	//10000011 - c
	printf("%d\n", c);
	//%d 十进制的方式打印有符号整数
	//11111111111111111111111110000011 - c的补码
	//11111111111111111111111110000010 - c的反码
	//10000000000000000000000001111101 - c的原码
	//-125
	//
	return 0;
}

b和c的值被提升为普通整型,然后再执行加法运算。
加法运算完成之后,结果将被截断,然后再存储于a中。

实例:

int main()
{
 char c = 1;
 printf("%u\n", sizeof(c));
 printf("%u\n", sizeof(+c));
 printf("%u\n", sizeof(-c));
 return 0;
}

结果为:1   4   4   4 

c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节

 12.2 算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

long double

double
float
nsigned long int

long int

unsigned int

int 

如果某个操作数的类型在上面这个列表中排名较低,
那么首先要转换为另外一个操作数的类型后执行运 算。

注意:
但是算术转换要合理,要不然会有一些潜在的问题。

float  f = 3.14;
int  num = f;    //隐式转换,会有精度丢失

12.3 操作符的属性

复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。

两个相邻的操作符先执行哪个?取决于他们的优先级。
如果两者的优先级相同,取决于他们的结合性。


网站公告

今日签到

点亮在社区的每一天
去签到