小边同学强势总结之C语言操作符

Posted 小边同学:)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小边同学强势总结之C语言操作符相关的知识,希望对你有一定的参考价值。

引:

操作符这部分内容呐,在小边眼里,是看似食之无味,弃之又非常非常可惜的一部分,而且其实是蛮有意思的。
这么讲是因为我个人认为,学校老师把它打散散布在零星各处又不够深入,而如果出题又可能“刁钻”,所以今天拿出来好好总结一下。

正文开始@边通书

一、操作符分类

二、算术操作符

+,-,* 都非常简单,唯一要注意的两点就是 乘 * 和 取模 %

除号 /


运行结果:

取模 %(整除之后求余数)

三、移位操作符

移位操作符移动的都是 内存中的 二进制位。
其实对于整数的二进制位有3种表示方法:原码反码补码,整数在内存中存储的都是补码


呀,怕小伙伴忘记先说一下,最高位也就是符号位,正数为0,负数为1奥。

左移操作符<<(相对简单)

1.左移原理剖析

1.左移正数a:

运行结果:

2.左移负数c:

运行结果

2.右移原理剖析

右移操作符>> (其实也没夺复杂啦)

(1)右移正数a:


运行结果:

(2)右移负数a:


运行结果:

可见,vs2013采用的是算术右移(即补符号位),或者说大多数编译器都采用算术右移。
且,算术右移似乎更合适一些,你是负数,右移之后仍为负数。

3.注:

可爱同学写的胡乱代码,如下


四、位操作符

1.原理剖析

按位与&

按位或|

按位异或^

2.典例

那么这些位操作符到底有什么用呢?下面看:

典例1:计算某整数存储在内存二进制位中有多少1?

1.方法一:

代码:

#include<stdio.h>

int main()
{
	int a = 15;
	int count = 0;
	int i = 0;

	for (i = 0; i < 32; i++)
	{
		if (((a >> i) & 1) == 1)
		{
			count++;
		}
	}

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

运行结果,如我所愿:

3.变态面试题

一道变态的面试题:不创建临时变量(第三个变量),实现两个数交换。

我们最最简单也是最常用的办法:

方法一:加减法

运行结果:

此方法虽满足不创建临时变量的要求,但是有一定问题的:
如果a,b都很大且还在int范围之内,buta = a + b很有可能溢出。依然不是最好的解决方案。

方法二:异或的方法

#include<stdio.h>

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

	a = a^b;
	b = a^b;
	a = a^b;

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

运行结果:

原理剖析:
异或这个操作符是很有意思的,它让我想起了集合论与图论里的对称差,那个小三角符号。

然而这种方法实际用的比较少:原因:
1.代码可读性不好,乍一看都不知道什么意思
2.只适用于整形
3.且执行效率低
优点是满足了面试官的需求哈哈哈哈。

五、赋值操作符

赋值操作符是一个很棒的操作符,可以让你得到一个之前不满意的值。
1.连续赋值

2.复合赋值符
a += 2;即为a = a + 2;
a >>= 1;即为a = a>>1;
a &= 1即为a = a & 1,
显然前者更加简洁。

类似的有这些:

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

六、单目操作符

单目操作符:即为只有一个操作数的操作符

逻辑反!

一般使用场景如下:

正号+ 负号-

比较简单,不多讲

取地址& 解引用*

sizeof

1.作用剖析 与 strlen()简单对比

运行结果:

2.sizeof是操作符,不是函数
面试官问你:“小伙子,sizeof是不是函数?”,你说,“是!”,面试官“孩子,回家吧。”哈哈哈哈哈

下面用代码简要说明一下:

3.sizeof 内部的表达式是不参与真实运算的
看看以下代码,小伙伴们觉得结果是什么?

#include<stdio.h>

int main()
{
	int a = 5;
	short s = 10;
	printf("%d\\n", sizeof(s = a + 2));//?
	printf("%d\\n", s);//?
	return 0;
}

运行结果:

原理剖析:

4.sizeof与数组
读以下代码,给出你的结果:

#include <stdio.h>

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

原理剖析:

运行结果:
32位平台:

按位取反~

二进制位全部取反,连符号位也要取反!!!
1.上代码感受一下

运行结果:

调试窗口,看看内存中~a的真实存在:

2.应用场景

++,- -

1.前置++

运行结果:

2.后置++

运行结果:

前置- -,后置- -,都同理,不赘述。
3.不建议研究过于复杂的相关代码

在vs下,运行结果为12;
然而在Linux平台下,gcc编译器算出结果为10,
那么这段代码本身就是错误的,就没有什么必要纠结,也不要写这样的代码。
至于为什么出现这种情况,后文会讲。

强制类型转换(类型)

七、关系操作符

同类型 的变量之间 比较大小,比较简单,不做赘述

<,>,<=,>=,==,!=

八、逻辑操作符

关注的是真假。

&&,||

1.逻辑与&&
360笔试题:逻辑操作符的短路效果

#include <stdio.h>
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; }

程序运行结果为多少?

剖析:左为假,则右边不再计算

举一反三:若将a的初值赋为1呐?

运行结果:

2.逻辑或||
完全类似的题目:

运行结果:

举一反三:若将a初值再改成0呐?

运行结果:

九、条件操作符

(exp1) ? (exp2) : (exp3)

十、逗号表达式

从左向右执行,整个表达式结果是最后一个表达式 的结果。

运行结果:

应用:

十一、下标引用,函数调用和结构成员

1.下标引用


进一步说明[ ] 是操作符:

运行结果:

2.函数调用

3.结构成员


上代码加强理解:

运行结果:

注:

十二、表达式求值

操作符可以说是为表达式而服务的,也影响着表达式的结果,主要体现在以下两方面:
1.表达式求值的顺序 ~ 操作符的优先级结合性
2.类型转换 ~ 操作数在求值过程中可能需要转化为其他类型

1.隐式类型转换

隐式,即偷偷地,没法实在地看到。
这里要提到整形提升的概念及意义。

1.整形提升是什么

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

2.整形提升的意义
小边从来不喜欢贴大段文字,但在这里觉得很有必要,有助于理解为什么要整形提升。

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

这可能也是寄存器读写很快的原因。

上代码感受:
看看你觉得结果是多少?

#include<stdio.h>

int main()
{
	char a = 3;
	char b = 127;
	char c = a + b;
	printf("%d\\n", c);
	return 0;
}

运行结果:

有了前面的铺垫,看到这个结果也不会感到震惊。

原理剖析:


巩固练习1:

#include<stdio.h>

int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");
	return 0;
}

运行结果:只打印c

原理剖析:

巩固练习2:

运行结果:

2.算术转换

寻常算数转换:如果某个操作符的各个操作数属于不同的类型,那么其中一个操作数的必须转换为另一个操作数的类型,否则操作就无法进行。

代码感受:

转换规则:

3.操作符属性


关于优先级,这里值提供部分,有个感觉就好,记不住就加括号:

4.一些问题表达式

那是不是掌握了操作符的优先级及结合性及是否控制求值顺序,就一定能得到表达式的唯一结果了呐?也不是滴。下面来看一些问题表达式。

(1)代码1

a*b + c*d + e*f;

由于*+的优先级高,只能保证,*的计算是比 +早,但是优先级并
不能决定第三个*比第一个+早执行。变量之间的牵连,会导致结果可能不同。

(2)代码2:

(3)代码3:

不同编译器下跑出结果不同:
求值应有唯一路径,那么这样代码就没什么意义。

(4)代码4


(5)代码5
很早小边就拿出过这段代码了

Linux平台下,编译结果

vs2013平台下,编译结果

调试起来,转到反汇编,简单看看即可:

本文完

相信小伙伴们会收获满满的。

以上是关于小边同学强势总结之C语言操作符的主要内容,如果未能解决你的问题,请参考以下文章

C 语言学习 第二次作业总结

C语言之文件操作08——总结

C语言之文件操作总结

课程设计必备之数据库操作代码模板

个人作业4——alpha阶段个人总结(201521123003 董美凤)

C语言之文件操作(上)