深入浅出C语言——操作符

文章目录

  • 一、操作符分类
  • 二、算术操作符
  • 三、移位操作符
  • 四、位操作符
  • 五、赋值操作符
  • 六、单目操作符
  • 七、关系操作符
  • 八、逻辑操作符
  • 九、条件操作符
  • 十、逗号表达式
  • 十一、下标引用操作符
  • 十二、函数调用操作符
  • 十三、结构成员访问操作符
  • 十四、表达式求值


一、操作符分类

1.算术操作符
2.移位操作符
3.位操作符
4.赋值操作符
5.复合赋值操作符
6.单目操作符
7.关系操作符
8.逻辑操作符
9.条件操作符
10.逗号操作符
11.下标引用、函数调用和结构成员

二、算术操作符

+ - * / %
  • +-*这三个操作符与平常数学中运算规则相同,除法操作符/代表取整运算, % 代表取模运算。
  • 在这些运算符中,除了%运算符之外,其他的操作符都可以用于整数和浮点数,而%运算符只能操作整数
  • 对于/操作符而言,如果操作符的操作数都为整数,则执行整数除法。而只要有浮点数执行的就是浮点数除法。

三、移位操作符

  • 整数在内存中存储的是补码(二进制),整数的二进制有三种表现形式:原码 反码 补码

正整数

对于正整数而言,**正整数的原码、反码、补码是相同的。**例如:int a = 5; 一个整形占4个字节、32个比特位,换算为二进制(原码)为 : 00000000000000000000000000000101 ,则5的原码、反码、补码相同,都为: 00000000000000000000000000000101 ,开头的第一个数字表示正负, 0表示正数 ,1表示负数。

负整数

对于负整数而言,负整数原码、反码、补码是要计算的。例如:int a=-5;换算为二进制(原码)为 10000000000000000000000000000101 。反码:原码的符号位不变,其他数字按位取反: 11111111111111111111111111111010 反码加1就是补码: 11111111111111111111111111111011。


移位操作符

操作符 功能
<< 左移操作符
>> 右移操作符
  • 移位操作符的操作数只能是整数,并且移动的是二进制位补码,移动负数位是标准未定义的。
  • 移动的是补码,改变的是数据在内存中的存储结构,但是打印时还是按照原码进行打印。
  • 对一个数移位操作完成后,当前的数不会改变的,除非把它赋值给另外一个变量。
  • 左移操作相当于给之前的数乘2,右移操作相当于给之前的数除2。

<< 左移操作符

移位规则:左边丢弃, 右边补0。 例如: 5<<1,相当于补码整体向左移动一个字符。

深入浅出C语言——操作符_第1张图片


>> 右移操作符

右移运算分两种,分别是逻辑移位和算术移位。

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

还是用-5举例:
11111111111111111111111111111011
逻辑右移后:
01111111111111111111111111111101

2.算术移位:左边用原该值的符号位填充,右边丢弃

11111111111111111111111111111011
算术右移后:
11111111111111111111111111111101

vs系列编译器在右移的时候,采用的是算术右移,到底是算术右移还是逻辑右移取决于编译器。


四、位操作符

操作符 功能
& 按位与
按位或
^ 按位异或

位操作符的操作数必须是整数,且操作的位是二进制位补码

  • 对于&操作符,全1为1,否则为0。
  • 对于|操作符,有1为1,否则为0。
  • 对于^操作符,相同为0,不同为1。对于任意整数a, a^a=00^a=a

& 按位与

深入浅出C语言——操作符_第2张图片


| 按位或

深入浅出C语言——操作符_第3张图片


^ 按位异或

深入浅出C语言——操作符_第4张图片


不能创建临时变量,实现两个数的交换

#include 
int main()
{
	int a = 5; //101
	int b = 3; //011
	a = a ^ b; //a=110
	b = a ^ b; //b=101  原理:b=a^b^b=a^0=a
	a = a ^ b; //a=011  原理:a=a^b^a=b
	printf("a = %d b = %d\n", a, b);
	//局限性——异或操作符只能用于整数,这个方法不适用于字符操作。
	return 0;
}

求一个整数存储在内存中的二进制中1的个数

//方法一:
#include
int main()
{
	int num = 0;
	scanf("%d", &num);
	int count = 0;
	int i = 0;
	for ( i = 0; i < 32; i++)
	{
		if (1 == ((num >> i) & 1)) //一个数字&1,如果得到1,说明这个数字的最后一位是1
		{
			count++;
		}
	}
	printf("%d", count);
	return 0;
}
//方法二:
#include 
int main()
{
	int num = -1;
	int count = 0;	//计数
	while (num)
	{
		count++;
		num = num & (num - 1);
	}
	printf("二进制中1的个数 = %d\n", count);
	return 0;
}

五、赋值操作符

赋值操作符 =

int weight = 120;	//体重
weight = 89;		//不满意就赋值

复合赋值符

+= -= *= /= %= >>= <<= &= |= ^=
int x = 10;
x = x+10;
x += 10;	//复合赋值,//其他运算符一样的道理。这样写更加简洁。

六、单目操作符

单目操作符也就是只接受一个操作数的操作符。

深入浅出C语言——操作符_第5张图片


C99中引入了布尔类型,这样和0、1相比可读性更高。使用逻辑反操作符可以得到相反的布尔类型。

深入浅出C语言——操作符_第6张图片


  • sizeof 是一个操作符,不是函数,可以求变量(类型)所占空间的大小,单位是字节。
  • 特殊的是sizeof(long)在32位下为4,而在64位下为8, 这个取决于编译器的实现,C语言只是规定sizeof(long)>=sizeof(int)
  • 在C语言中的,**sizeof操作符中括号内的表达式不参与计算。
#include 
void test1(int arr[])  //本质上传过来是int* arr
{
	printf("%d\n", sizeof(arr));//  4/8 
	//数组首元素的地址用指针变量来存储,指针变量的大小是4/8,取决于计算机
}
void test2(char ch[]) //本质是传过来是 char* ch
{
	printf("%d\n", sizeof(ch));//4/8 
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr)); // 40   ——数组的大小10*4
	printf("%d\n", sizeof(ch));	 //10   ——数组的大小10*1
	test1(arr);
	test2(ch);
	return 0;
}

~ 操作符表示对一个数的二进制按位取反 ,即二进制每一位取反。

  • scanf()去读失败的时候会返回EOF,EOF本质上是-1,~ -1对-1二进制位取反得到0;
  • 0为假就停下来了,也正是这个原因,while(~scanf("%d",&n)可以终止循环。

++和–运算符

++a, 前置++,先++后使用。a++,后置++,先使用,后++,前置后置-- 同理。


强制类型转换

int a =int3.14;	//3

七、关系操作符

操作符 功能
> 大于
>= 大于等于
< 小于
!= 用于测试“不相等”
== 用于测试“相等”

判断字符串相等的时候,if ('abc' == 'afg')是错误的,这样比较的是字符串首字符的地址。正确的做法是用 strcmp 函数来比较字符串的大小,自左向右逐个按照ASCII码值进行比较,直到出现不同的字符或遇’\0’为止。


八、逻辑操作符

操作符 名称
&& 逻辑与
|| 逻辑或
  • 对于&&操作符,如果左边为假,就不用算右边了
  • 对于||操作符遇到表达式为真,直接输出结果就不执行后面的操作了。

深入浅出C语言——操作符_第7张图片


九、条件操作符

条件表达式的一般形式:表达式1 ? 表达式2:表达式3。此操作符有三个操作数,所以是三目操作符。

int a = 3;
int b = 6;
int c = 0;
c = a > b ? a : b;
//a > b 为表达式1,a为表达式2,b为表达式3
//该语句的意思为:如果a>b,则将a的值赋给c,否则将b的值赋给c

十、逗号表达式

exp1, exp2, exp3, …expN

  • 逗号表达式就是用逗号隔开的多个表达式。
  • 从左向右依次执行代码,整个表达式的结果是最后一个表达式的结果
int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
	printf("%d", c); 						  //13
	return 0;
}

十一、下标引用操作符

[] 下标引用操作符

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

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

十二、函数调用操作符

( ) 函数调用操作符

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

十三、结构成员访问操作符

  • 用来访问一个结构成员。

深入浅出C语言——操作符_第8张图片


十四、表达式求值

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


隐式类型转换

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

整型提升的意义

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

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

如何进行整体提升

整形提升是按照变量的数据类型的符号位来提升的。

深入浅出C语言——操作符_第9张图片

int main()
{
	char a = 0xb6;//10110110
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)//1011011000000000
		printf("b");
	if (c == 0xb6000000)
		printf("c");
	return 0;
}
/*实例1中的a,b要进行整形提升,但是c不需要整形提升
a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真.*/

深入浅出C语言——操作符_第10张图片


算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。

long double
double
float
unsigned long int
long int
unsigned int
int
//但是算术转换要合理,要不然会有一些潜在的问题
float f = 3.14;
int num = f;//隐式转换,会有精度丢失

操作符的属性

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

  1. 操作符的优先级。

  2. 操作符的结合性。

  3. 是否控制求值顺序。

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


如果写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。

//表达式的求值部分由操作符的优先级决定。
//表达式1
a*b + c*d + e*f
//注释:代码1在计算的时候,由于*比+的优先级高,只能保证,*的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行

//表达式2
c + --c;
/*注释:同上,操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得
知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。*/

//代码3-非法表达式
int main()
{
	int i = 10;
	i = i-- - --i * (i = -3) * i++ + ++i;
	printf("i = %d\n", i);
	return 0;
}
//表达式3在不同编译器中测试结果都不同

//代码4
int fun()
{
	static int count = 1;
	return ++count;
}
int main()
{
	int answer;
	answer = fun() - fun() * fun();
	printf("%d\n", answer);//输出多少?
	return 0;
}
/*虽然在大多数的编译器上求得结果都是相同的。
但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,再算减法。函数的调用先后顺序无法通过操作符的优先级确定。*/

//代码5
#include 
int main()
{
	int i = 1;
	int ret = (++i) + (++i) + (++i);
	printf("%d\n", ret);//VS:12  Linux:10
	printf("%d\n", i);
	return 0;
}

你可能感兴趣的:(C语言,c语言,开发语言)