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

C语言操作符详解

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

C语言操作符详解

目录

操作符分类:

算术操作符

 移位操作符

原码

反码

补码 

 左移操作符

 用负数进行左移

 右移操作符

 错误写法

位操作符

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

 编写代码实现:求一个整数存储在内存中的二进制中1的个数。

赋值操作符 

单目操作符

~按位取反讲解 

 sizeof 和 数组

 关系操作符

逻辑操作符

条件操作符

逗号表达式 

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

 ( ) 函数调用操作符

  访问一个结构的成员 

 表达式求值

隐式类型转换

整型提升的意义:

算术转换 

操作符的属性 

 操作符优先级


操作符分类:
算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用、函数调用和结构成员

算术操作符

+      -      *      /      %

1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。 2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。 3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

 

 移位操作符

<< 左移操作符 >> 右移操作符     注:移位操作符的操作数只能是整数。

原码

计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数 该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。

反码

正数的反码是它本身,负数的反码是符号位不变,其它位取反

补码 

整数的补码是它本身,负数的补码是反码+1,整数是以补码的形式进行存储的

 左移操作符

 此时a的数值并未发生变化。而是把操作完的数字赋值给了b

 用负数进行左移

总结:左移操作符,左边丢弃,右边补0

 右移操作符

对a右移一位之后,我们发现此时符号位的位置空缺了一位,此时应该补0或者1 

算数位移:左边丢弃,右边补原符号位

逻辑位移:左边丢弃,右边补0

我们用负数测试后,发现vs2019用的是算术位移, 因为逻辑位移只补0,补0之后就是正数了,大多数编译器用的都是算术位移,

 错误写法

 

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

 

 

 

 负数运算也是如此,按位与&:有0则结果为0

                                 按位或| :有1则结果为

                                  按位异或^:不同则为1

运算法则:补码相运算,然后把补码根据原码反码补码的关系,转为原码,则最终得到的数字就是原码所代表的数字,只不过是以补码的形式进行存储

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

#include
int main()
{
	int a = 3;
	int b = 5;
		printf("交换前:%d %d", a, b);
		a = a + b;
		b = a - b;
		a = a - b;
		printf("交换后:%d %d", a, b);

	return 0;
}

 

#include
int main()
{
	int a = 3;
	int b = 5;
		printf("交换前:%d %d", a, b);
		a = a ^ b;
		b = a ^b;
		a = a ^b;
		printf("交换后:%d %d", a, b);

	return 0;
}

#include
int main()
{
	int a = 3;
	int b = 5;
		a = 3^ 3;
		b = 3 ^0;
		printf("%d %d", a, b);

	return 0;
}

任何数和0异或都为任何数,任何数和它本身异或都为0  

异或支持交换律 

 编写代码实现:求一个整数存储在内存中的二进制中1的个数。

 

#include
int main()
{
	int a = 3;
	int sum = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((a >> i) & 1)
		{
			sum++;
		}
	}
	printf("%d", sum);
	return 0;
}
#include
int main()
{
	int num = 10;
		int count = 0;//计数
	while (num)
	{
		if (num % 2 == 1)
			count++;
		num = num / 2;
	}
	printf("二进制中1的个数 = %dn", count);
	return 0;
}
#include 
int main()
{
 int num = -1;
 int i = 0;
 int count = 0;//计数
 while(num)
 {
 count++;
 num = num&(num-1);
 }
 printf("二进制中1的个数 = %dn",count);
 return 0; }

赋值操作符 
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。

int weight = 120;//体重 weight = 89;//不满意就赋值 double salary = 10000.0; salary = 20000.0;//使用赋值操作符赋值。 赋值操作符可以连续使用,比如: int a = 10; int x = 0; int y = 20; a = x = y+1;//连续赋值 x = y+1; a = x;
复合赋值: += -= *= /= %= >>= <<= &= |= ^=

单目操作符

!            逻辑反操作
-负值
+正值
&取地址操作符
sizeof操作数的类型长度
~对一个数的二进制进行按位取反
--前置、后置--
++前置、后置++
*间接解引用操作符
(类型) 强制类型转换

~按位取反讲解 

 按位取反是按照补码进行取反,之后转换到原码,输出的是原码的值

 

若想把红色位置的0变为1,我们可进行下面的操作 

将1的二进制位左移1位,然后进行按位或

 

 当我们想把下面的红色1,改为0,也就是恢复上面的操作时,我们可这样做

如何得到最后一行数字? 我们对下面这个数字按位取反即可

 

 

 

 sizeof 和 数组

 

size of是操作符,不是函数,size of()的括号可以去除,但是在使用的时候建议加上() 

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

 ++a与a++

 关系操作符
> >= < <= !=   用于测试“不相等” ==      用于测试“相等

比较字符串是否相等时不能用==,用strcmp函数,头文件是sting.h

逻辑操作符
&&     逻辑与 ||          逻辑或
&& 左边为假,右边就不计算了
|| 左边为真,右边就不计算了

 

条件操作符

 exp1 ? exp2 : exp3

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

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

 [ ] 下标引用操作符

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

 

arr[7]=7[arr]=*(arr+7)=*(7+arr) ,但是定义数组的时候不能把arr[10]写作10[arr]。

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

 

  访问一个结构的成员 
. 结构体.成员名 -> 结构体指针->成员名
#include
#include 

struct Stu                    //创建结构体类型
{
	char name[20];
	int age;
	double score;
};

void set_stu(struct Stu* ps)
{
	//strcpy((*ps).name, "zhangsan");
	//(*ps).age = 20;
	//(*ps).score = 100.0;

	strcpy(ps->name, "zhangsan");
	ps->age = 20;
	ps->score = 100.0;
}

void print_stu(struct Stu* ps)
{
	printf("%s %d %lfn", ps->name, ps->age, ps->score);
}

int main()
{
	struct Stu s = { 0 };
	set_stu(&s);          //进行传参,这里采取了传地址
	print_stu(&s);        //同样进行传地址   

	return 0;
}

 访问结构体有俩种形式.和->

 表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。

同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

整型提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度

一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长

度。

通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令

中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转

换为int或unsigned int,然后才能送入CPU去执行运算。

 

我们可以看到5+126结果为-125,这是因为发生了整形提升 

所以这里可以看到C输出的是负数。这是因为char类型占一个字节8个比特位,而在运算的时候,CPU一般处理int类型,而int类型占四个字节,32个比特位,此时8个比特位显然不够,编译器会自动补充其余24个比特位,补充的内容为符号位,无符号整形高位补0

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,这是因为a,b要进行整形提升,但是c不需要整形提升 a,b整形提升之后,变成了负数

 c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字

节.

表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节

算术转换 

 如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类

型,否则操作就无法进行。下面的层次体系称为寻常算术转换

long double

double

float

unsigned long int

long int

unsigned int

int

 优先级自上而下逐渐降低,但存在精度丢失的风险

操作符的属性 

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

1. 操作符的优先级

2. 操作符的结合性

3. 是否控制求值顺序。

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

 操作符优先级

 

#include
int main()
{
	int a = 1;
	int b = 2;
	int c = 3;
	int d = 4;
	int e = 5;
	int f = 6;
		int h=a * b + c * d + e * f;
		printf("%d", h);
	return 0;
}

 这种运算方式,只能保证,*的计算是比+早,但是优先级并不 能决定第三个*比第一个+早执行

c + --c;  

操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得

知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。(先算--c,然后算加法的时候不能保证C是--c之前的值还是--c之后的值)

#include
int main()
		{
			int a = 1;
			int b = (++a) + (++a) + (++a);
			printf("%dn", b);
			return 0;	
        }

 不能保证先算哪个括号里面的,还有无法确定先算哪一个+

int main()
{
 int i = 10;
 i = i-- - --i * ( i = -3 ) * i++ + ++i;
 printf("i = %dn", i);
 return 0; }

编译器会凌乱

int fun()
{
     static int count = 1;
     return ++count; }
int main()
{
     int answer;
     answer = fun() - fun() * fun();
     printf( "%dn", answer);//输出多少?
     return 0; }

但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法, 再算减法。 函数的调用先后顺序无法通过操作符的优先级确定。

 

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

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

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