C语言-操作符和表达式详解

本章重点

详细介绍各种操作符,对操作符加深了解
对表达式求值的一些注意事项

操作符

算术操作符

      • / %

对于除号,保留的是商,取模保留的是余数

在这里插入图片描述

#include <stdio.h>

int main()
{
	int n = 7;
	printf("%d\n", 7 / 2);
	//除号操作数有一个是浮点数,就执行的是浮点数的除法
	printf("%.2lf\n", 7 / 2.0);	
	//printf("%d\n",7/0);		不能除0,会报错
	//printf("%d\n",7%2);		不能%0,也会报错
	//printf("%d\n", 7 % 5.0);	不能用取模计算浮点数,会报错,取模操作符两端只能是整数
	return 0;
}

移位操作符

左移操作符<<
右移操作符 >>

左移操作符

正数

#include <stdio.h>

int main()
{
	int a = 1;
	int b = a << 1;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}
//b结果为2,移动一位结果是a的值乘以2
//a<<1,a的值没有变,还是1

在这里插入图片描述
负数

#include <stdio.h>

int main()
{
	int a = -1;
	a = a << 1;
	printf("%d\n", a);
	//10000000000000000000000000000001	-1的原码
	//11111111111111111111111111111110	-1的反码,反码是原码按位取反得到的,最高位不变,其他位取反
	//11111111111111111111111111111111  -1的补码,补码是反码加一得到的
	
	//表示在屏幕上的是十进制,从内存中拿出来是原码
	//左移一位
	//11111111111111111111111111111110	补码
	//11111111111111111111111111111101	反码
	//10000000000000000000000000000010	原码-2
	return 0;
}
//结果为-2
右移操作符

算术右移

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

逻辑右移

右边丢弃,左边补0

#include <stdio.h>

int main()
{
	int a=-1;
	printf("%d\n",a>>1);
	//结果为-1
	//11111111111111111111111111111111		-1的补码
	//向右移动1位,根据结果反推,是算术右移
	//右边丢弃,左边补0,结果还是-1
	return 0;
}
//除了-1,其他的数向右移动1位,结果为原来的值除以2

对于左移和右移不要移动移动位

位操作符

& 按位与
| 按位或
^ 按位异或
注:他们的操作数必须是整数。

& 按(二进制)位与
#include <stdio.h>

int main()
{
	int a = 3;
	int b = 5;
	int c = a & b;
	//00000000000000000000000000000011
	//00000000000000000000000000000101

	//00000000000000000000000000000001
	//按位与,两个数同为1则1,其他为0
	
	printf("%d\n", c);
	return 0;
}
//结果为1
| 按位或
#include <stdio.h>

int main()
{
	int a = 3;
	int b = 5;
	int c = a | b;
	//00000000000000000000000000000011
	//00000000000000000000000000000101

	//00000000000000000000000000000111
	//按位或,有1则1
	
	printf("%d\n", c);
	return 0;
}
//结果为7
^ 按位异或
#include <stdio.h>

int main()
{
	int a = 3;
	int b = 5;
	int c = a ^ b;
	//00000000000000000000000000000011
	//00000000000000000000000000000101

	//00000000000000000000000000000110
	//按位异或,相同为0,相异为1
	
	printf("%d\n", c);
	return 0;
}
//结果为6
交换两个整数

1.临时变量

#include <stdio.h>

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

	int tmp = a;
	a = b;
	b = tmp;

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

2.不创建临时变量

#include <stdio.h>

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

	a = a + b;		//8
	b = a - b;		//3
	a = a - b;		//5

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

这种会造成溢出

3.用异或操作符

#include <stdio.h>

int main()
{
	int a = 3;
	int b = 5;
	//都是正数,就算计最后一个字节,好看出结果
	//00000011	a
	//00000101	b
	
	a = a ^ b;		//00000110	a^b		a=6	
	b = a ^ b;		//00000011	a^b		b=3
	a = a ^ b;		//00000101	a^b		a=5

	printf("%d %d\n", a, b);
	return 0;
}
求一个整数在内存中存放的二进制有几个1
int main()
{
	int i = 0;
	int number = 0;
	int count = 0;

	scanf("%d", &number);

	for (i = 1; i <= 32; i++)
	{
		if (((number>>i) & 1) == 1)
		{
			count++;
		}
	}
	printf("%d\n", count);
	return 0;
}

赋值操作符

int a = 10;	//初始化
a = 20;		//赋值

连续赋值

int a=10;
int b=20;
int c=20;
a=b=c+1;	//从右向左运算
复合操作符

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

#include <stdio.h>

int main()
{
	int a = 1;
	a += 1;		//a=a+1;
	a *= 2;		//a=a*2;
	return 0;
}

单目操作符

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

int main()
{
	int flag = 0;
	if (!flag)
	{
		printf("表示真\n");
	}
	else
	{
		printf("表示假\n");
	}
	return 0;
}
//!flag flag=0,为假,加上!,表示真
-和+ 单目操作符
#include <stdio.h>

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

	a = +a;			//很少用,不会改变负数的值,正数前面默认有+
	printf("%d\n", a);
	return 0;
}
&和* 单目操作符
#include <stdio.h>

int main()
{
	int a = 10;		//& 取地址操作符	- 把a的地址拿出来
	int* p = &a;	//int* 表示整型指针,p是变量
	*p = 20;		//指针是用来存放地址的,把a的地址放在指针变量p里面
					//* 解引用操作符-间接访问操作符
					//*p-表示通过指针变量p里存放的地址找到变量a的值
	printf("%d\n", a);
	printf("%d\n", *p);
	return 0;
}
sizeof 操作符
#include <stdio.h>

int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof a);		//这种情况可以省略
	//printf("%d\n", sizeof int);		//不能省略
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof(&a));

	int arr[10] = { 0 };
	printf("%d\n", sizeof(arr));	//整个数组的大小
	printf("%d\n", sizeof(int[10]));//数组的类型
	return 0;
}

在这里插入图片描述

s是short类型,a+5是四个字节,会发生截断
sizeof里面的表达式是不计算的
sizeof(s=a+5)在编译期间就算出来了
a+5的运算是在程序运行时计算的

练习

在这里插入图片描述

~
#include <stdio.h>

int main()
{
	int a = 1;
	//00000000000000000000000000000001
	//11111111111111111111111111111110		//按位取反
	//
	//10000000000000000000000000000010		//-2的原码
	//11111111111111111111111111111101		//反码
	//11111111111111111111111111111110		//补码
	printf("%d\n", ~a);
	return 0;
}
scanf的多组输入
#include <stdio.h>

int main()
{
	int a=0;
	while(~scanf("%d",&a))
	{
		;
	}
	return 0;
}

当scanf返回EOF(-1)时,在按位取反~得到0,终止循环

前置++操作符
#include <stdio.h>

int main()
{
	int a = 0;
	printf("%d\n", ++a);
	printf("%d\n", a);
	return 0;
}
//1 1
//前置++,前++后使用
后置++操作符
#include <stdio.h>

int main()
{
	int a = 0;
	printf("%d\n", a++);
	printf("%d\n", a++);
	return 0;
}
//0和1
//后置++,先使用后++
强制类型转换
#include <stdio.h>

int main()
{
	int a = (int)3.14;

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

关系操作符

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

逻辑操作符

&& 逻辑与
|| 逻辑或
//多用在if里面

在这里插入图片描述

练习题
#include <stdio.h>

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

	printf("i = %d\na = %d\nb = %d\nc = %d\nd = %d\n", i, a, b, c, d);
	return 0;
}
// 0 1 2 3 4
//&& 从左向右运算,左边为假后边不执行
#include <stdio.h>

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

	printf("i = %d\na = %d\nb = %d\nc = %d\nd = %d\n", i, a, b, c, d);
	return 0;
}
// 1 1 3 3 4
//左边为真,右边不执行

条件操作符

exp1 ? exp2 : exp3

#include <stdio.h>

int main()
{
	int a = 3;
	int b = 5;
	printf("%d\n", a > b ? a : b);
	return 0;
}
//如果a大于b,输出a的值,否则输出b的值

逗号表达式

从左到右依次计算,最后一个表达式是结果

#include <stdio.h>

int main()
{
	int a = 3;
	int b = (a = a + 1, a = a + 2, a = a + 1);

	printf("%d\n", b);
	return 0;
}
//7

下标引用操作符

#include 

int main()
{
	int arr[10]={0};
	arr[4]=5;		//把5赋值给数组的第5个元素
	return 0;
}

函数调用操作符

#include <stdio.h>
void test()
{
	printf("hello world\n");
}
int main()
{
	test();		//()函数调用操作符
	return 0;
}

结构成员访问操作符

#include <stdio.h>
struct Stu
{
	char name[20];
	int age;
	char sex[5];
};
int main()
{
	struct Stu s = { "zhangsan",18,"man" };
	printf("%s\n",s.name);
	printf("%d\n", s.age);
	printf("%s\n", s.sex);

	struct Stu* p = &s;
	printf("%s\n", (*p).name);

	printf("%s\n", p->name);

	return 0;
}

表达式求值

注意优先级和结合性

隐式类型转换

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

cpu内整型运算器的操作数的字节长度一般是int的字节长度,同时也是CPU的通用寄存器的长度,因此char类型的相加,会变成int型相加

#include <stdio.h>

int main()
{
	char a = 3;
	char b = 127;
	char c = 0;
	c = a + b;
	//整型提升补原符号位
	//00000011
	//00000000000000000000000000000011	a
	//01111111
	//00000000000000000000000001111111	b
	//00000000000000000000000010000010	a+b
	//10000010
	//打印出来的是int类型,在发生提升
	//11111111111111111111111110000010	补码
	//11111111111111111111111110000001	反码
	//10000000000000000000000001111110	原码	  -  -126
	printf("%d\n", c);
	return 0;
}
//-126

无符号数高位补0

#include <stdio.h>

int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
	{
		printf("a\n");
	}
	if (b == 0xb600)
	{
		printf("b\n");
	}
	if (c == 0xb6000000)
	{
		printf("c\n");
	}
	return 0;
}
//打印c	a,b会发生整型提升
#include <stdio.h>

int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));
	printf("%u\n", sizeof(-c));
	//printf("%u\n", sizeof(!c));	!c不算数据运算

	return 0;
}

算术转换

long double
double
float
unsigned long int
long int
unsigned int
int
// 转换是向着高字节转换
//比如int和long int会转换成long int
float f = 3.14;
int num = f;//隐式转换,会有精度丢失

操作符的属性

对于复杂表达式的求值要注意下面三个问题:

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序。
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
//这种在不同的编译器下计算的结果也不一样
#include <stdio.h>

int fun()
{
	static int count = 1;
	return ++count;
}
int main()
{
	int answer = 0;
	answer = fun() - fun() * fun();
	printf("%d\n", answer);
	return 0;
}
// -10
//这也是有问题的,-10是2-3*4
//也可以是4-2*3=-2
#include <stdio.h>

int main()
{
	int i=1;
	i=(++i)+(++i)+(++i);
	printf("%d\n",i);
	return 0;
}
//在vs2019上结果为12,先计算三个++i,i=4,然后三个4相加
//如果在linux上结果为10,先算计前两个++i,i=3,前两个i相加
//6+(++i)=10

表达式要有唯一计算路径

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值