C语言操作符与表达式详解

目录

操作符的分类:

(1)算数操作符

(2)移位操作符

(3)位操作符

(4)赋值操作符

(5)单目操作符

(6)关系操作符

(7)逻辑操作符

(8)条件操作符

(9)逗号表达式

(10)下标引用、函数调用和结构成员

表达式求值:

隐式类型转换

整型提升

算术转换

操作符的属性

运算符优先级和结合性表格:

一些问题表达式


操作符的分类:


(1)算数操作符

+(加) -(减) *(乘 ) /(除) %(取余)

1.除了%操作符之外,其他的几个操作符可以作用于整数和浮点数

2.对于/操作符如果两个操作数都为整数,执行整数除法,而只要其中有一个浮点数执行的就是浮点数除法

cpp 复制代码
int main() {
	/*int a = 6, b = 5;
	printf("%d", a / b);*/      //打印结果为1

	double a = 6.0;
	int b = 5;
	printf("%lf", a / b);      //打印结果为1.200000
	return 0;
}

3.%操作符的两个操作数必须为整数,返回的是整除之后的余数


(2)移位操作符

正数在内存中用原码(因与补码相同)表示,负数用补码

<< 左移操作符

移位规则:左边抛弃,右边补0

cpp 复制代码
	int a = 2;
	//把a的二进制位向左移动一位(0010->0100)
	int b = a << 1;        //并不改变a的值
	printf("%d",b);        //打印结果为4

>>右移操作符

移位规则:

1.逻辑移位:左边用0补充,右边丢弃

2.算数移位:左边用原来的符号位填充,右边丢弃

cpp 复制代码
int a = 10;
	//把a的二进制位向右移动一位(1010->0101)
	int b = a >> 1;         //并不改变a的值
	printf("%d", b);        //打印结果为4

(3)位操作符

& //按位与

cpp 复制代码
	int a = 3;
	int b = 5;
	//& - 按(2进制)位与
	int c = a & b;
	//0011 -a
	//0101 -b
	//0001 -c(对应位上同为1才为1,否则为0)
	printf("%d", c);        //打印结果为1

| //按位或

cpp 复制代码
    int a = 3;
	int b = 5;
	//| -按(2进制)位或
	int c = a | b;
	//0011 -a
    //0101 -b
    //0111 -c(对应位上同为0才为0,否则为1)
	printf("%d", c);        //打印结果为7

^ //按位异或

cpp 复制代码
	int a = 3;
	int b = 5;
	// ^ -按(2进制)位异或
	int c = a ^ b;
	//0011 -a
	//0101 -b
	//0110 -c(对应位相同为0,相异为1)
	printf("%d", c);        //打印结果为6

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

异或操作的用途之一:不创建中间变量即可交换两个整型元素的值(同时保证交换过程中不发生范围溢出),例:

cpp 复制代码
	//不创建中间变量交换a和b的值
	int a = 3;
	int b = 5;
	printf("a=%d, b=%d\n",a,b);
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("a=%d, b=%d\n",a,b);

(4)赋值操作符

= += -= *= /= >>= <<= %=

连续的等号赋值是从左至右依次赋值:

cpp 复制代码
	int a = 20;
	int x = 0;
	int y = 20;
	a = x = y + 1;
	printf("%d %d",a,x);//打印结果为21  21

复合赋值符的含义:

cpp 复制代码
	int a = 10;
	a = 100;
	a += 100;//与a = a + 100等价
	//其他复合赋值符同理

注:C语言中一个=是赋值,两个=是判断


(5)单目操作符

! 逻辑反操作

cpp 复制代码
	int flag = 5;//为真
	printf("%d\n", !flag);//打印结果为0(为假)
	flag = 0;//为假
	printf("%d\n", !flag);//打印结果为1(为真)

- 负值

将某数变为其相反数

cpp 复制代码
int a=10;
a=-a;
printf("%d",a);//打印结果为-10

+ 正值

对操作数不做何改变

cpp 复制代码
int a=10;
a=+a;
printf("%d",a);//打印结果为10

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

注: sizeof是一个操作符,不是函数

cpp 复制代码
	int a = 10;
	int arr[10] = { 0 };
	printf("%d\n", sizeof(arr));//计算arr所占空间大小,单位是字节,打印结果为40,因为int类型大小是4个字节,数组有10个元素
	printf("%d\n", sizeof(int));//打印结果是4,可以直接对类型用sizeof

sizeof括号内的表达式不参与运算

cpp 复制代码
	short s = 5;
	int a = 10;
	printf("%d\n", sizeof(s = a + 2));//打印结果为2(short类型大小为2个字节)
	printf("%d\n", s);//打印结果为5,sizeof括号内的表达式不参与运算

~ 对一个数的二进制按位取反(包括符号位)

cpp 复制代码
	int a = -1;
	//10000000000000000000000000000001 -原码
	//11111111111111111111111111111110 -反码
	//11111111111111111111111111111111 -补码
	// ~ 按位取反
	//00000000000000000000000000000000(补码按位取反)
	int b= ~a;
	printf("%d",b);//打印结果为0

++ 前置、后置++

后置++:

cpp 复制代码
	int a = 10;
	int b = a++;//后置++,先使用,再++

	printf("%d\n", a);//打印结果为11
	printf("%d\n", b);//打印结果为10

前置++:

cpp 复制代码
	int a = 10;
	int b = ++a;//前置++,先++,后使用
	printf("%d\n", a);//打印结果为11
	printf("%d\n", b);//打印结果为11

- - 前置、后置- -

原理同++

cpp 复制代码
int a = 10;
printf("%d\n", a--); //打印结果为10
printf("%d\n", a);//打印结果为9

* 间接访问操作符(解引用操作符)、 & 取地址操作符

cpp 复制代码
int a = 10;
      printf("%p\n", &a);//& - 取地址操作符
      int *pa = &a;//pa是用来存放地址的 - pa就是一个指针变量
      *pa = 20;// * 解引用操作符 - 间接访问操作符
      printf("%d",a);//打印结果为20,*pa就代表着a

(类型) 强制类型转换

cpp 复制代码
      int a = (int)3.14;//将3.14double类型变量强制转换为int类型
	  printf("%d", a);//打印结果为3

(6)关系操作符

>

>=

<

<=

!= 用于测试"不相等"

== 用于测试"相等"

常用于判断语句中,较为简单


(7)逻辑操作符

&& 逻辑与

|| 逻辑或

cpp 复制代码
    int a = 3;
	int b = 0;
	if (a && b) {//a和b同时为真时,才进入下面的语句
		printf("hello");//不执行此语句
	}
	if (a || b) {//a和b有一个为真,就进入下面的语句
		printf("world");//执行此语句
	}

:区分逻辑与按位与 区分逻辑或按位或

1&2---------->0 (0001和0010----->0000)

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

1|2----------->3 (0001和0010----->0011)

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

注:(表达式)1&&(表达式2)时,若(表达式1)=0,则(表达式2)不再计算

cpp 复制代码
    int i = 0 , a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
	printf("%d\n%d\n%d\n%d\n", a, b, c, d);//打印结果为 1 2 3 4

注:(表达式)1||(表达式2)时,若(表达式1)=1,则(表达式2)不再计算

cpp 复制代码
	int i = 0, a = 1, b = 2, c = 3, d = 4;
	i = a++ || ++b || d++;
	printf("%d\n%d\n%d\n%d\n", a, b, c, d);//打印结果为 2 2 3 4

(8)条件操作符

又称作三目操作符

exp1 ?exp2 : exp3

exp1结果为真返回exp2,否则返回exp3

cpp 复制代码
	int a = 3;
	int b = 0;
	//三目操作符
	b = (a > 5 ? 1 : -1);//表达式1 ?表达式2 :表达式3
	printf("%d", b);//打印结果为-1

(9)逗号表达式

exp1, exp2, exp3, ...expN

逗号表达式 - 要向右依次计算,但是整个表达式的结果是最后一个表达式的结果

cpp 复制代码
    int a = 3;
	int b = 5;
	int c = 0;
	//逗号表达式要向右依次计算,但是整个表达式的结果是最后一个表达式的结果
	int d = (c = 5, a = c + 3, b = a - 4, c += b);
	printf("%d",d);//结果为9

(10)下标引用、函数调用和结构成员

[ ] : 下标引用操作符

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

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

( ): 函数调用操作符:接受一个或多个操作数

第一个 操作数是函数名,剩余的操作数就是传递给函数的参数

cpp 复制代码
	int Add(int x, int y) {//函数的定义
		return x + y;
	}
cpp 复制代码
	int a = 10;
	int b = 20;
	int ret = Add(a, b);//调用函数  () - 函数调用操作符

访问一个结构的成员:

. 结构体.成员名

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

cpp 复制代码
	//结构体
	//书:书名,书号,定价
	//创建了一个自定义的类型
	struct Book
	{
		//结构体的成员变量
		char name[20];
		char id[20];
		int price;
	};


	int num = 10;
	struct Book b = { "C语言","20191112537",55 };
	//结构体变量.成员名
	printf("%s\n", b.name);
	printf("%s\n", b.id);
	printf("%d", b.price);
cpp 复制代码
	//结构体指针->成员名
	struct Book* pb = &b;
	printf("%s\n", pb->name);
    printf("%s\n", pb->id);
    printf("%d", pb->price);
    //输出结果与上方代码输出结果相同

表达式求值:

表达式求值的顺序一部分是由操作符的优先级和决定性决定。

同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。


隐式类型转换

C的整型算数运算总是至少以缺省整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为

整型提升

(只有长度小于int长度(short、char)才进行整型提升)

cpp 复制代码
	char a, b, c;
	//...
    a = b + c;

b和c的值被提升为普通整型,然后再执行加法运算。

加法运算完成之后,结果被截断,然后再存储于a中。

整型提升的意义:

具体例子:

cpp 复制代码
    char a = 3;
	//00000011 - a(char只能存8个bite位)
	char b = 127;
	//01111111 - b
	char c = a + b;
	//a和b都是char类型,都没有达到一个int的大小
	//这里会发生整型提升,高位补的都是符号位(无符号统一补0)

	//a整型提升后:00000000000000000000000000000011
	//b整型提升后:00000000000000000000000001111111
	//          c=00000000000000000000000010000010 
	//c发生截断,只存8个bite位,c = 10000010
	//此时c是char类型的变量,需要打印整型变量,再次发生整型提升
	//c - 11111111111111111111111110000010     (高位补符号位1)
	//但负数在内存中形式是补码,所以还要再求c的原码
	//c的原码 - 10000000000000000000000001111110 = -126
	printf("%d\n", c);//打印结果为-126
cpp 复制代码
    char c = 1;
	printf("%u\n", sizeof(c));//1
	printf("%u\n", sizeof(+c));//4
	printf("%u\n", sizeof(-c));//4
	printf("%u\n", sizeof(!c));//4
	//%u是输出无符号整型数
	//+c  -c  !c  参与了运算,发生了整型提升,所以变为int的大小

算术转换

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

long double

double

float

unsigned long int

long int

unsigned int

int

上方从大至小排列

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

cpp 复制代码
	int a = 4;
	float f = 4.5f;
	a + f;//需要将a的类型转换为float类型去计算

注:算数转换要合理,不然存在一些潜在的问题

cpp 复制代码
	float f = 3.14;
	int num = f;//会丢失精度

操作符的属性

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

1.操作符的优先级

2.操作符的结合性

3.是否控制求值顺序(如(表达式1)&&(表达式2):(表达式1)是0后(表达式2)不再计算))

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

cpp 复制代码
	int a = 4;
	int b = 5;
	int c = a + b * 7;//优先级决定了计算顺序
	int c = a + b + 7;//优先级不起作用,结合性决定

运算符优先级和结合性表格:

(引用自博主星空之路Star的博客C语言34种运算符优先级及结合性

一些问题表达式

注:有些表达式的运算顺序可能还是不唯一的

cpp 复制代码
//表达式1
//a* b + c * d + e * f
//
//表达式的计算机可能是:
//a * b
//c * d
//a * b + c * d
//e * f
//a * b + c * d + e * f
//
//或者
//a * b
//c * d
//e * f
//a * b + c * d
//a * b + c * d + e * f

//由于*比+的优先级高,只能保证*的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行
cpp 复制代码
	//表达式2
    int c=3; 
	c + --c;
    //优先级只能保证--运算在+之前,无法得知+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果不可预测,有歧义
    //2+2=4 或 3+2=5
cpp 复制代码
	//表达式3
	int i = 1;
	int ret = (++i) + (++i) + (++i);
	printf("%d\n", ret);// VS里打印出12       gcc打印出10

    //因为VS中先算了三次++i,再将三个结果相加
    //而gcc中先算了两次++i,再相加,再执行++i,再相加(与上方表达式1原理类似)

以上为C语言中操作符和表达式相关内容的详细讲解,感谢您花费宝贵的时间阅读本文章!

相关推荐
霍格沃兹测试开发学社测试人社区7 分钟前
软件测试学习笔记丨Flask操作数据库-数据库和表的管理
软件测试·笔记·测试开发·学习·flask
今天我又学废了24 分钟前
Scala学习记录,List
学习
杨荧28 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
白子寰35 分钟前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
XuanRanDev44 分钟前
【每日一题】LeetCode - 三数之和
数据结构·算法·leetcode·1024程序员节
代码猪猪傻瓜coding1 小时前
力扣1 两数之和
数据结构·算法·leetcode
王俊山IT1 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。1 小时前
c++多线程
java·开发语言
小政爱学习!1 小时前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript