你必须知道的关于操作符的那些事

经过了五一假期的放松,我们重新投入到编程学习大军中,Lets’ go!


算术操作符

+ - * / %

当我们进行除法操作时
int a = 9 / 2; //4
得到的是商,不会得到4.5
这是为什么呢?

这是因为/旁边都是整数,执行的是整数除法,即使我们把它改成float a = 9 / 2也没有改变它的本质----还是将整数除法的结果赋给a;如果我们要将表达式的结果正确显示出来,我们可以这样做:

float a = 6 / 5.0;
//或者
float a = 6.0f / 5.0f;
//只要有其中一个数为浮点数,其结果就为浮点数

int a = 7 % 3.0;
printf("%d\n", a);// error
在这里插入图片描述

  • %两边操作数必须为整数

移位操作符

之前在我的 第二篇C语言初级基础知识中粗略介绍到移位操作符是将二进制位移动的,今天我们来具体讲一讲。

左移右移的操作方法:

  • 左移操作符:左边抛弃,右边补0
  • 右移操作符:
    逻辑移位:左补0,右丢弃
    算术移位:左补原符号位值,右丢弃

    在这里插入图片描述
    (当右移一位时)

我们用一个例子来介绍这两种移位方法的区别:
在这里插入图片描述
-1以补码形式存放在内存中(正整数原反补码相同)
在这里插入图片描述
当补码向右移动一位时,符号位补1
最终a的值与右移后b的值一致,证明该操作使用的是算术右移。

  • 大部分情况下,使用的是算术右移

位操作符

&   //按位与
|   //按位或
^   //按位异或
//皆按二进制位 - 比较对应位

&的用途

当我们想要改变二进制中某一位数时,使用&可以方便的让我们修改数字。
在这里插入图片描述
修改第五位数为0,可以让第五位数&上0,该数就改变为0;其它位& 1 不变。但是现在又有了一个问题:我们如何得到第二个二进制序列呢?
在这里插入图片描述
通过使用按位取反~和1<<4得到

  • ^规则:相同为0,相异为1
  • 操作数皆为整数

用途

我们来看一道笔试题:

交换两个·int型变量的值,不使用第三个变量。

第一种方法:

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;
	printf("a = %d b = %d\n", a, b);
	return 0;
}

但是,这种方法有缺陷----数字过大溢出

第二种方法:

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;
	printf("a = %d b = %d\n", a, b);
	return 0;
}

采用异或来进行两个数的交换。通过交换二进制位达到效果,我们可以这样来理解:

1.任意一个变量x与其自身进行异或运算,结果为0,即x^x=0
2.任意一个变量x与0进行异或运算,结果不变,即x^0=x

将第二种方法中的b = a ^ b看作b = a ^ b ^ b,因为异或运算原理为:相异为1,相同为0,所以b ^ b为0 ,0 ^ a为 a,因此a的值就赋给了b。

练习

下面来做一道练习:
实现:求一个整数存储在内存中的二进制中1的个数

赋值操作符

= += -= *= /= %= >>= <<= &= |= ^=
a = x = y + 1;//连续赋值

单目操作符

! - + & sizeof ~ -- ++ * (类型)

!的用途

int main()
{
	int flag = 5;
	if (flag)
		printf("hehe\n");
	if (!flag)
		printf("haha\n");
	return 0;
}

sizeof的用途

	int a = 10;
	int arr[10] = { 0 };
	
	//计算数组大小
	pritnf("%d\n", sizeof(arr));
	pritnf("%d\n", sizeof(int [10]));

	//计算a大小
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof a);//从这也可以看出sizeof是一个操作符,不是函数
	return 0;
}
	short s = 5;
	int a = 10;
	printf("%d\n", sizeof(s = a + 2));
	printf("%d\n", s);
	return 0;

请你想一想结果是多少?
在这里插入图片描述
a+2得到12,将其值赋给s,s并没有改变其类型 - short - 占2个字节。sizeof里面的表达式在编译处进行操作,运行时进行的是printf操作,所以实际上没有改变s的值。

  • sizeof括号里面的表达式不参加运算

sizeof与strlen的区别

#include<stdio.h>
int main()
{
	char str[] = "hello world";
	printf("%d %d\n", sizeof(str), strlen(str));//12 11
	return 0;
}
  • 计算字符串长度时,sizeof计算\0,strlen不计算\0
  • sizeof为操作符;strlen为函数,需要引用头文件

sizeof和数组

void test1(int arr[])
{
	printf("%d\n", sizeof(arr));
}

void test2(int ch[])
{
	printf("%d\n", sizeof(ch));
}

int main()
{
	int arr[10] = { 0 };
	int ch[10] = { 0 };
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(ch));
	test1(arr);
	test2(ch);
	return 0;
}

在这里插入图片描述
为什么经过了传参,数组的大小就变得不一样了呢?
数组传参,传的是数组首元素的地址,本质上为指针变量。指针变量的大小取决于系统,在32位平台上为4个字节,在64位平台上为8个字节

~的用途

int main()
{
	int a = -1;
	//对补码进行按位取反
	int b = ~a;
	printf("%d", b);//0
	return 0;	
}

  • 不改变a的值

++ --的用途

x++:后置++,先使用,再++
++x:前置++,先++,后使用

通过下面的代码来复习一下后置和前置的不同:

int main()
{
	int a = 10;
	int b = a++;
	printf("%d\n", a);//11
	printf("%d\n", b);//10

	int c = 10;
	int d = ++a;
	printf("%d\n", c);//11
	printf("%d\n", d);//11
//x--和--x同理
	return 0;
}

下面使用一个不建议使用的例子来考考你:

	int a = 1;
	int b = (++a) + (++a) + (++a);
	printf("%d", b);

结果为12
在这里插入图片描述
第一次++a:2
第二次++a:3
第三次++a:4(++a变成4)
最后计算括号外的+号:4+4+4=12

该代码在windows和linuxs环境下结果不同,故不建议采用它作例子

* &的用途

int main()
{
	int a = 10;
	printf("%p\n", &a);// & - 取地址操作符
	int* pa = &a;
	*pa = 20;// * - 解引用操作符 - 间接访问操作符
	printf("%d\n", a);//20
	return 0;
}
  • 要清楚&符号取地址按位与的不同

(类型)的应用

int main()
{
	int a = (int)3.14;
	printf("%d", a);//3
	return 0;
}

关系操作符

> < <= >= !=
  • 清楚 = 和 == 的不同

逻辑操作符

&&
||

&&的用途

#include<stdio.h>
int main()
{
	int a = 3;
	int b = 0;
	if (a && b)
	{
		printf("hehe");      //不输出
	}
	//if (a || b)
	//{
	//	printf("hehe");     //hehe
	//}

	return 0;
}

a&&b:只有a和b同时为真时才为真
a | | b:只要有一个为真即为真

曾经有一道笔试题:

int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
	return 0;
}

在这里插入图片描述
那么这段代码结果又是什么?

int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ || ++b || d++;
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
	return 0;
}

通过这两段代码的结果,可以知道:

&&:只要第一个表达式为假,后面表达式都不再计算。

| |:只要第一个表达式为真,后面表达式不再计算

条件操作符

? :

用法:

	int b = a > 5 ? 3 : -1;

a>b则结果为3,否则为-1;

逗号操作符

int main()
{
	int a = 3;
	int b = 5;
	int c = 0;
	int d = (c = 5, a = c + 3, b = a - 4, c += 5); 
	printf("%d", d);  //10
	return 0;
}

从左向右依次计算,最后一个表达式结果为整个表达式结果

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

[ ] - 下标引用操作符
( ) - 函数调用操作符
. ->  - 结构成员访问操作符

[ ] 的用途

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

[ ] 的操作数为2个:arr , 10

( )的用途

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 20;
	int ret = Add(a, b); //
	printf("%d", ret);
	return 0;
}

. ->的用途

struct Book
{
	char name[20];
	char id[10];
	int price;
};
1.
int main()
{
	int num = 10;
	struct Book b = { "C语言","1234654",55 };
	printf("书名:%s\n", b.name);
	printf("书号:%s\n", b.id);
	printf("定价:%d\n", b.price);
	return 0;
}
2.
int main()
{
	struct Book * pb = &b ;
	printf("书名:%s\n", (*pb).name);
	printf("书号:%s\n", (*pb).id);
	printf("定价:%d\n", (*pb).price);
	return 0;
}	
3.
int main()
{
	struct Book * pb = &b ;
	printf("书名:%s\n", pb->name);
	printf("书号:%s\n", pb->id);
	printf("定价:%d\n", pb->price);
}

结构体指针->成员名

隐式类型转换

在这里插入图片描述
整型提升时,如果操作数类型不同,则它们会被转换为下列层次中出现的最高层次的类型

▲整型提升

整型提升是什么呢?我们通过下面的代码来理解:

#include<stdio.h>
int main()
{
	char a = 3;
	char b = 127;
	char c = a + b;
	printf("%d\n", c);
	return 0;
}

在这里插入图片描述
??!为什么会是-126呢?
在这里插入图片描述
此代码便是整型提升的体现。

3的二进制序列为: 00000000000000000000000000000011
存到a - (char类型)中,只存了8个比特位:
00000011
同理,b也一样:
01111111

在这里插入图片描述
正数的整型提升:
高位补充0 - 即符号位
负数的整型提升:
高位补充1 - 即符号位
无符号整型的整型提升:
高位补0

补齐后再相加,为:
00000000000000000000000010000010
注意:c为char类型,所以需要截断,即:
10000010 - c
提升后为:
11111111111111111111111110000010 - 补码
打印出来的应该是原码:
11111111111111111111111110000001 - 反码
10000000000000000000000001111110 - 原码 ( -126)

以上,即为整型提升的过程。

示例

int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)//c本身就是整型,不会发生整型提升
		printf("c");
	return 0;
}

在这里插入图片描述

▲算术转换

#include<stdio.h>
int main()
{
	int a = 4;
	float f = 4.5f;
	//a + f;
	return 0;
}

计算 a+f (不同类型)时,要将int类型的a转换为另一个要进行操作的相同类型,这就称为寻常算术转换

操作符优先级

同一优先级的运算符,运算次序由结合方向所决定。

C语言运算符的优先级和结合性一览表.

错误代码示范

一.

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

计算顺序有多种可能性。容易导致错误。

  1. List item
  2. 在这里插入图片描述

二.

c + --c;

三.

int main()
{
	int i = 10;
	i = i-- - --i * i++ + ++i;
	printf("i = %d\n", i);
	return 0;
}

此代码在不同编译器结果不同。

四.

int main()
{
	int i = 1;
	int ret = (++i) + (++i) + (++i);
	printf("%d\n", ret); //12 - 在linux环境下为10
	printf("%d\n", i); //4
	return 0;
}

以后在写代码时,要避免写出此类代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值