栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言操作符

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言操作符

目录

1.算数操作符

2.移位操作符

3.位操作符

4.赋值操作符

5.单目操作符

6.关系操作符

7.逻辑操作符

8.条件操作符

9.逗号表达式

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

11.隐式转换*


插曲:符号位也是参与运算的。

1.算数操作符

+    -   *    /   %

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

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

两个整数除,最终结果还是整数double打印也是4.000000。

为什么打印的不是4.500000,所以把2改成2.0就成为了浮点数。

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

2.移位操作符

<< 左移操作符

>> 右移操作符

注意:移位操作符移动的是二进制位(二进制补码)。

左移操作符移位法则:

左边抛弃,右边补0

右移操作符移位法则:

首先右移运算分为两种:

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

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

3.位操作符

& 按位与

| 按位或

^ 按位异或 

注意:它们的操作数必须是整数。符号位也参与运算。

异或的题目:异或具有交换律

不能创建临时变量(第三个变量),实现两个数交换。

#include 

int main()
{
	int a = 5;
	int b = 10;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("a=%d  b=%d", a, b);
	return 0;
}

4.赋值操作符

=

赋值操作符可以连续使用:

int main()
{
	int a = 10;
	int x= 0;
	int y = 20;
	a = x = y + 1;
	return 0;
}

等价于:

int main()
{
	int a = 10;
	int x= 0;
	int y = 20;
	x = y + 1;
	a = x;
	return 0;
}

复合赋值符 

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

int x = 10;
x = x + 10;
//这两句等价上边符合运算符也是如此
x += 10;

5.单目操作符

! 逻辑取反

-   负值

+   正值

&   取地址

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

~   对一个数的二进制按位取反(取反符号位也参与运算)

--  前置、后置--

++ 前置、后置++

*  间接访问操作符(解引用操作符)

(类型) 强制类型转换

说明一下:左值和右值问题

左值:一般就是使用变量的空间

右值:一般就是指空间的内容

1.sizeof

sizeof是计算变量或者类型创建变量的内存大小,和内存中存放什么数据没有关系,sizeof是一个操作符,不是函数。

证明sizeof不是函数

int main()
{
	int a = 10;
	printf("%d",sizeof(a));
	printf("%d", sizeof a);
	return 0;
}

结果都是4,4个字节. 

sizeof代码分析:

int main()
{
	int a = 10;
	printf("%dn",sizeof(a=a+10));
	printf("%d",a);
	return 0;
}

结果: 

sizeof内表达式不参与运算,编译阶段就已经计算好了,不进行链接。因此a=a+10不执行,直接算变量的内存大小所以位4,10。

sizeof和数组

void test1(int arr[])
{
	printf("%dn", sizeof(arr));
}
void test2(char ch[])
{
	printf("%dn", sizeof(ch));
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%dn", sizeof(arr));
	printf("%dn", sizeof(ch));
	test1(arr);
	test2(ch);
	return 0;
}

结果:

首先40和10:数组单独放在sizeof内部时,数组名表示整个数组,计算整个数组的大小。

4和4:函数中数组的传参,其实只是传进去了指针,int arr[]==int* arr。地址的大小4/8个字节。

2.强制类型转换

强制类型转换的意义:保证前后数据类型的一致,防止警告。

int a=3.14;

会出现警告,为了保证前后数据类型一致性,并取消掉警告,需要使用强制类型转换。

int a=(int)3.14;

这样就不会出现警告了。

6.关系操作符

>、>=、<、<=、!=、== 

这里就只需要注意:==别写成=,说多了都是泪。

7.逻辑操作符

&& 逻辑与

||   逻辑或

区分按位与&、或 ||和逻辑与&&、或||

代码分析:

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

	printf("a = %dn b = %dn c = %dnd = %dn", a, b, c, d);
	return 0;
}

结果: 

首先在执行 i = a++ && ++b && d++; 时,a时后置++先使用在++;因此在a在&&的的时候是0,逻辑与一个为假,结果为假,当为假的时候后面就不在执行。所以结果为1,2,3,4

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

	i = a++||++b||d++;

	printf("a = %dn b = %dn c = %dnd = %dn", a, b, c, d);
	return 0;
}

这个代码与上边的代码正好是相反的,当为真时停止后边的计算,a后置++,所以a在执行||时为0,a在++ a=1;继续执行++b 前置++,x先++在使用,b++等于3为真,因此结果为 1 3 3 4. 

8.条件操作符

exp1 ? exp2 : exp3 //C语言唯一个三目操作符

exp1表达式是否成立,成立结果为exp2,否则为exp3。

9.逗号表达式

exp1,exp2,exp3,....expN

逗号表达式,就是用逗号隔开的多个表达式。逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

代码分析:

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, b = a + 1);
	printf("%dn",a);
	printf("%dn",b);
	printf("%dn",c);
}

逗号表达式应用的意义: 

a = get_val();
count_val(a);
while (a > 0) {
         //业务处理
        a = get_val();
        count_val(a);
}

等价于
while (a = get_val(), count_val(a), a>0) {
         //业务处理
}
if (a =b + 1, c=a / 2, d > 0)

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

1.[ ]下标引用操作符

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

int arr[10];

[ ]操作符是arr和9.

2.()函数调用操作符

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

3.访问一个结构成员

结构体.成员名

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

都是操作符。。。

11.隐式转换*

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

1.整型提升

在C语语言中,C的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前都被转换围殴普通的整型(int),这种转换称为整型提升。(其实整型提升主要针对的是short和char。)

整型提升的意义:

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

整型提升代码分析:

#include 

int main()
{

	char a = -2;
	char b = 1;
	char c = a + b;
	printf("%dn",c);
	return 0;
}

 注意:无符号的整型提升,高位补0;

整型提升的例子:

#include 

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");
	}
}

结果:

其实当进行操作符进行各类计算时,都会发生整型提升的隐式转换。也就是说这里的在进行==号进行操作符运算时,会发生整型提升,其实这个整型提升是需要看后面的==后面比较的数是否为整型。个人理解就是:需不需要整型提升是需要看操作符各个操作数属不属于相同类型,如果不属于就转换成层次体系高的:也就是所谓的算术转换。

2.算术转换

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

#include 

int main()
{
	float i = 3.14;
	int num = i;
}

这里发生了截断数据,直接把3.14截断成整数3进行存储。所以有时算术转换要合理,要不然后发生一些潜在的问题。

3.操作符的属性:

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

1.操作符的优先级(只决定相邻的操作符,相邻的操作符看优先级)

2.操作符的结合性。

3.是否控制求值顺序,也就是所说的: &&   ||

根据操作符优先级:

一些问题表达式:这些问题需要小心且避免。

1.

a*b+c*d+e*f

根据优先级,*比+的优先级高,所以相邻的还是先算*再算加,但是会出现2中情况:

第一种是先算完乘法在进行+法,都满足优先级。

a*b

c*d

e*f

a*b+c*d+e*f

第二种是也是先算的*,也满足优先级

a*b

c*d

a*b+c*d

e*f

a*b+c*d+e*f

上述两种情况都满足优先级,但是无法确定第一个+先执行还是最后一个*先执行,所以程序存在二义性。

2.

C+ --C

首先 -- 的优先级比+的优先级高,先执行--,但是我们无法知道第一个c的值是取--c之前还是之后呢。

3.

#include 

int main()
{
	int i = 1;
	int ret = (++i) + (++i) + (++i);
	printf("%dn",ret);
	
}

这段代码在linux中是10,在windows中是12。所以程序有二义性。

分析windows汇编了解一下在windows中的过程:

 也就是 ++i 执行完成后在执行+。在Linux中执行过程就不一样。

4.

#include 

int fun()
{
	static int count = 1;
	return ++count;
}
int main()
{
	int answer;
	answer = fun() - fun() * fun();
	printf("%dn", answer);
	return 0;
}

这段代码大部分编译器都是:-10,但是这段代码也有二义性,虽然 * 比 - 的优先级高,但是根本无法知道是 fun() 函数调用的先后顺序。

总结:可以建立多个变量或分开写,避免出现这种错误代码。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/698476.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号