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语言中操作符和表达式相关内容的详细讲解,感谢您花费宝贵的时间阅读本文章!

相关推荐
小此方14 分钟前
C语言自定义变量类型结构体理论:从初见到精通(上)
c语言·开发语言
努力也学不会java20 分钟前
【Java并发】揭秘Lock体系 -- 深入理解ReentrantReadWriteLock
java·开发语言·python·机器学习
lingggggaaaa20 分钟前
小迪安全学习笔记(一百零二讲)—— 漏扫项目篇&PoC开发&Yaml语法&插件一键生成&匹配结果&交互提取
笔记·学习·安全·网络安全·交互
里昆27 分钟前
【COMSOL】结构力学仿真(压缩弹性体)案例心得
学习
vxtkjzxt88833 分钟前
自动化脚本矩阵运营
开发语言·php
王严培.42 分钟前
7.MATLAB疑难问题诊疗的技术
开发语言·matlab·信息可视化
wjs20241 小时前
PHP MySQL 使用 ORDER BY 排序查询
开发语言
爱敲代码的TOM1 小时前
深入剖析Java通信架构下的三种IO模式2
java·开发语言·架构
UWA1 小时前
Unreal开发痛点破解!GOT Online新功能:Lua全监控 + LLM内存可视化!
开发语言·lua·unreal
肥肠可耐的西西公主2 小时前
后端(JavaWeb)学习笔记(CLASS 2):SpringBootWeb入门
笔记·学习