C语言拯救者 (操作符--6)

目录

1.1:算术操作符

2.1:移位操作符(操作数只能是整数)

3.1:位操作符(操作数必须是整数)

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

3.5:求两个数二进制中不同位的个数

4.1.例题练习:编写代码实现:求一个整数存储在内存中的二进制中1的个数。

5.1:赋值操作符

6.1:单目操作符

6.3:sizeof例题

6.3:单目操作符练习

7.1:关系操作符

8.1:逻辑操作符

8.2:&& 和 ||练习

9.1:条件操作符(三目操作符)

10.1:逗号表达式

11.1:下标引用,函数调用和结构成员

12.1:隐式类型转换

12.2:整型提升例题

12.3:算术转换

12.4:操作符的属性

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


1.1:算术操作符

+   -  *  / %

int main()
{
	float a = 3 / 2;
	printf("%f", a);//答案是1,错误写法
	return 0;
}



int main()
{
	float a = 3 / 2.0;//在整数后面加小数点即可,答案1.5
	printf("%f", a);
	return 0;
}


int main()
{
	float a = 3 % 2;
	printf("%d", a);//3/2...1,结果是1,得到的是余数
	return 0;
}

1:我们想得到结果1.5,结果却是1.000000,因为除号两边是整数,只能执行整数除法: 3/2余1

2:在任意整数上加上一个浮点数,按照小数除法执行

3:对于取模操作数来说,表达式左右值必须为整形


2.1:移位操作符(操作数只能是整数)

<<左移操作符  (移位规则:二进制中左边丢弃,右边补0)

>>右移操作符(移位规则:  1.算术右移:二进制中右边丢弃,左边补符号位)

2.逻辑右移:右边丢弃,左边补零

整数的二进制表示有3种形式:原码,反码,补码

正整数的原码、反码、补码是相同
负整数的原码、反码、补码是要计算的

整数在内存中存储的是补码的二进制

在二进制中,如果该数字是有符号数,最高位为符号位(0表示正数,1表示负数)

2.2例题:

int main()
{
	int a = -1;
  -1的二进制位  原码10000000000000000000000000000001;(第一位1表示负数)
               反码11111111111111111111111111111110 (原码符号位不变,其他位按位取反)
               补码11111111111111111111111111111111 (反码加1得到补码)

 负数的原反是补是算出来的
	return 0;
}

2.3例题:

int main()
{
	int a = 2;
	printf("%d",a << 1);
	return 0;
}


a写成二进制是00000000000000000000000000000010  
<<移动1位,遵循左边丢弃,右边补0,变成00000000000000000000000000000100,答案是4

2.4例题:

int main()
{
	int a = -2;
	printf("%d",a << 1);(实际上打印的是原码)
	return 0;
}


原码:10000000000000000000000000000010
反码:11111111111111111111111111111101
补码:11111111111111111111111111111110   (a在内存中存的二进制序列)

<<1一位,11111111111111111111111111111110变成11111111111111111111111111111100


11111111111111111111111111111100(补码)

11111111111111111111111111111011(-1得到反码)

10000000000000000000000000000100  (答案是4)

注意:a的值不会发生改变

2.5:>>右移操作符

int main()
{
	int a = 5;  //00000000000000000000000000000101
	int b = a >> 1;//00000000000000000000000000000010
	printf("%d %d",a,b);
	return 0;
}



int main()
{
	int a = -5; 
原码:10000000000000000000000000000101
反码:11111111111111111111111111111010
补码:11111111111111111111111111111011


	int b = a >> 1;
补码(采用算术右移,补原符号位):11111111111111111111111111111101
反码(-1):11111111111111111111111111111100
原码:10000000000000000000000000000011


答案:b=-3

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

当前编译器,右移执行的是算术右移,算数右移还是逻辑右移取决于编译器

移位运算符不要移动负数位,这是标准未定义


3.1:位操作符(操作数必须是整数)

&   按位与(二进制)对应的二进制位:两个都为1,结果为1,否则为0

|    按位或(二进制)对应的二进制位:有1为1,同时为0才为0

^  按位异或(二进制)//对应的二进制位:相同位为0,相异为1

3.2:练习

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

    //00000000000000000000000000000011  -> 3的补码
	//11111111111111111111111111111011  -> -5的补码


	int c = a & b;

   //00000000000000000000000000000011   
   答案是3



	int c = a | b;
    //11111111111111111111111111111011
    答案是-5

	int c = a ^ b;
    //11111111111111111111111111111000
    //11111111111111111111111111110111
    //10000000000000000000000000001000
    答案是-8

 

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

 3.3.练习:有一个数组只有一个数字出现过一次,剩余数字都是成对出现,找出只出现过一次的数组(自己动手写下)

提示:1.一个数和它自己异或,对应二进制位相同,结果为0

2. 0和任何数异或都是它自己

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

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

	printf("a=%d b=%d\n", a, b);
	
	a = a + b;
	b = a - b;
	a = a - b;  //写法1,数字太大会出错

    
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;  //写法二  

a=011(二进制位) b=101

a=a^b  (a=110)
b=a^b  (b=011) 此时,a的内容是110,b的内容变成011
a=a^b  (a=101) 你会发现,异或后a和b二进制颠倒了

也可以这么理解,把a=a^b代入b=a^b中, 变成b=b^b^a,由于b^b=0, 0^a=a,相当于把a放到b中

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

	return 0;
}

3.5:求两个数二进制中不同位的个数


int NumberOf1(int n)

{
	int count = 0;
	while (n)

	{
		n = n & (n - 1);
		count++;
	}
	return count;

}

int main()

{

	int m = 0;
	int n = 0;
	scanf("%d%d", &m, &n);
	int count = 0;
	int ret = m ^ n;
	count = NumberOf1(ret);
	printf("%d", count);

	return 0;

}


4.1.例题练习:编写代码实现:求一个整数存储在内存中的二进制中1的个数。

int main()
{
	int a = 5;
	scanf("%d",&a);
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if (((a >> i) & 1) == 1)  //a&1,得到最低位是否为1
			count++;
	}
	printf("%d", count);
	return 0;
}

5.1:赋值操作符

int main()
{
	int weight =120;
    weight =100;//赋值修改
    

    int a = 10;
    int x = 0;
    int y = 20;
    a=x=y+1;//连续赋值
    //先执行x=y+1,最后x=21,a=21  不推荐这种写法
    


	return 0;
}

5.2:复合赋值符(+=  -=  *=  /=  %=  >>=   <<=  &=  |=  ^=)

int main()
{
	int a = 10;
	a = a + 2;
	a += 2;//等价

	a = a >> 1;
	a >>= 1;//等价

	a = a & 4;
	a &= 4;//等价

	return 0;
}

6.1:单目操作符

!  逻辑反操作符

+ -    正负值

&    取地址

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

~   对一个数二进制位按位取反

++  有前置,后置

--   有前置,后置

*   解引用操作符

(类型)  强制类型转换

6.2:例题

struct S
{
	char name[20];
	int age;
};

int main()
{
	//& 取地址操作符
	//* 解引用操作符(间接访问操作符)
	
	int a = 10;
	int* pa = &a;//把a的地址放在int*指针中
	*pa = 20;//* - 解引用操作符

	int arr[10] = {0};
	&arr;//取出数组的地址,数组的地址应该放到【数组指针】中去

	struct S s = {0};
	struct S* ps = &s;
	
	return 0;
}

6.3:sizeof例题

sizeof是一个操作符,不是函数
计算类型创建的变量所占内存的大小,单位是字节


int main()
{
	int a = 10;
	printf("%d\n", sizeof a);//可以省略阔号
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(int));//当它是类型不可以省略

	int arr[10] = { 0 };
	printf("%d\n", sizeof(arr));  //40个字节

	int a = 10;
	short s = 0;
	printf("%d\n", sizeof(s = a + 2));//打印出来是2,a还是10,sizeof()中的表达式不参与计算

	printf("%d\n", s);//s的值是0,s=a+2,a是整形,把整形放到短整型中还是短整型            

	return 0;
}

sizeof在求表达式结果,在编译期间就求出来了

void test1(int arr[])
{
printf("%d\n", sizeof(arr));//  4/8传过来的是int*指针,指针占内存空间4/8字节
}

void test2(char arr[])
{
printf("%d\n", sizeof(arr));//  4/8传过来的是char*指针,指针占内存空间4/8字节
}

int main()
{
	
	int arr1[10] = { 0 };
	printf("%d\n", sizeof(arr));//40

   char  arr2[10] = { 0 };
	printf("%d\n", sizeof(arr));//10


   test1(arr1);
   test2(arr2);

	return 0;
}


6.3:单目操作符练习


int main()
{
	int a = 3;
	int b = ++a;//前置++,先++,后使用//a=a+1,b=a
	int b = a++;//后置++,先使用,后++。//b=a,a=a+1
	int b = --a;//前置--,先--,后使用 //a=a-1,b=a
	int b = a--;//后置--,先使用,再-- //b=a,a=a-1

	return 0;
}



int main()
{
	int a = (int)3.14;//类型不匹配,把3.14浮点型数字强制类型转换成int类型数据
	printf("%d\n", a);

	return 0;
}

7.1:关系操作符

>

>=

<

<=

!=   (测试相等)

==  (测试相等)  字符串之间不能用==比较是否相等,应该用strcmp   

注意:应该随时注意是否为=(赋值)还是==(判断相等)


8.1:逻辑操作符

&&  逻辑与

||     逻辑或


int main()
{
	int a = 1;
	int b = 0;
	if (a && b)
	{
     printf("hehe\n");//跟数学中的且,或一样,两个同时满足为真
	}
	if (a || b)
	{
		printf("hehe\n");一个满足为真
	}
	return 0;
}

8.2:&& 和 ||练习

#include <stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;

	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    //答案是1,2,3,4   
    左边&& a++后置,先计算后++,为假,后面不计算,只计算了a++ 

    int i = 0, a = 1, b = 2, c = 3, d = 4;
    i = a++ || ++b || d++;
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); 
    //答案是2,2,3,4,a为真后面不计算


	return 0;
}

9.1:条件操作符(三目操作符)

exp1?exp2:exp3

int main()
{
	
	if (a > 5)
		b = 3;
	else
		b = -3;

     写成:

	b = (a > 5 ? 3 : -3);//a>5 ?  是为判断1,否为判断2

    
   找出两个数中最大的数字
   int a = 3;
   int b = 0;
   int m = (a > b ? a : b);



	return 0;
}


10.1:逗号表达式

逗号表达式,从左至右依次执行,整个表达式执行的结果是最后一个表达式的结果

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
	printf("a=%d b=%d\n", a, b);
	printf("%d\n", c);
	return 0;
}

答案是a=12,b=13,c=13

a>b不产生影响,表达式为假 ->a=12 -> b=13  把13赋值给c

11.1:下标引用,函数调用和结构成员

[ ]下标引用操作符   


int main()
{
	
       int arr[10] = { 0 };
    printf("%d\n", arr[1]);
    printf("%d\n", 4[arr]);
    printf("%d\n", *(arr + 1));//等价写法

 
	return 0;
}

() 函数调用操作符


int main()
{
	int ret = Add(2, 3);//()函数调用操作符,操作数就是:Add,2,3
	printf("%d\n", ret);

	return 0;
}

struct Stu
{
	char name[20];
	int age;
	float score;
};


//结构体变量.成员名
void print1(struct Stu ss)
{
	printf("%s %d %f\n", ss.name, ss.age, ss.score);
}


//结构体指针->成员名(指针接收)
void print2(struct Stu* ps)
{
	//printf("%s %d %f\n", (*ps).name, (*ps).age, (*ps).score);
	printf("%s %d %f\n", ps->name, ps->age, ps->score);

int main()
{
	struct Stu s = {"张三", 20, 90.5f};


	print1(s);
	print2(&s);
	return 0;
}


12.1:隐式类型转换

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

同样,有些表达式操作数在求值时可能需要转换成其他类型

C语言计算至少是以整数进行计算

通用CPU (general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

int main()
{
	char c1 = 3;
	//00000011 - c1
	//00000000000000000000000000000011
	char c2 = 127;
	
	//01111111 - c2
	//00000000000000000000000001111111
	
    char c3 = c1 + c2;
	
    c1+c2是char类型,需要整型提升(按照符号位提升)
    //00000000000000000000000000000011
	//00000000000000000000000001111111
	//00000000000000000000000010000010
	//10000010 - c3  char类型截断,只能存八个比特位,最高位为负数
	//11111111111111111111111110000010
	//11111111111111111111111110000001
	//10000000000000000000000001111110
	//-126

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

12.2:整型提升例题

int main()
{
	char a = 0xb6;//10110110
	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同理

int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));//1
	printf("%u\n", sizeof(+c));//4
	printf("%u\n", sizeof(-c));//4
	return 0;
}
  

c参与了表达式运算,发生了整型提升

12.3:算术转换

如果操作符的各个操作数属于不同类型,除非其中一个操作数的转换为另一个操作数的类型,否则操作无法进行,算术转换是按照类型排名来转换

如果该类型小于int,char和short会整形提升,跟大于等于int类型进行算术转换

long double
double
float
unsigned long int
long int
unsigned int
int


unsigned int 和int 类型一起,int转换为unsigned int类型


float f = 3.14;
int num = f;//隐式类型转换,float精度丢失

12.4:操作符的属性

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

1.操作符的优先级

⒉操作符的结合性

3.是否控制求值顺序。(&& || 三目操作符)

int main()
{
	int a = 10;
	int b = 20;
	int c = a + b * 5;//*优先级高
    int c = a + b + 5;//优先级一样看结合性,+号结合性从左到右
	return 0;
}

知道操作符优先级,结合性,求值顺序,也不一定能求出表达式的唯一计算路径

a*b+c*d+e*f

无法知道是怎么样的求值路径,表达式就可能存在问题



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

//不确定,我们只知道* 和- ,*先算,但是不知道fun()函数调用是谁先,结果就有问题,错误代码

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

  • 45
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北方留意尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值