初阶C语言|操作符详解

一、操作符分类

算术操作符
移位操作符、位操作符(这两者都是对二进制补码操作)
赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式
下标引用、函数调用和结构成员

二、操作符详解

2.1算术操作符

有+ - * / %

1.除了%操作符之外,其他操作数可以是整数或者浮点数,%的两个操作数必须是整数。返回的是整除之后的余数
2.对于/操作符的两个数是两个整数的话,执行整数除法,否则只要有一个是浮点数,执行浮点数除法。

2.2移位操作符

<< :左移操作符 >>:右移操作符

注:移位操作符的操作数只能是整数,计算的是二进制补码
数据在内存存储的是补码,计算的是补码,但最终打印出的结果是原码的十进制的值
移位之后,本身的值是没有改变的,例如:

#include <stdio.h>
int main()
{
    
    
	int num = 10;
	num << 1;
	printf("%d", num);
	return 0;
}

在这里插入图片描述

原码?反码?补码?
例如:int num=10;
000000000000000000001010–原码
011111111111111111111110101–反码
011111111111111111111110110–补码
都是以二进制形式表示
反码则是在原码的基础上,符号位不变(最高位为符号位,正数的符号位为0,负数的符号位为-1),其他位按位取反,补码则是在反码的基础上,在加个1即可,在二进制位中的计算规则是,逢2进1,跟数学的逢10进1是一个道理,数学的计算也是计算机中10进制的计算。

2.2.1左移操作符

<<移位规则:左边抛弃、右边补0
在这里插入图片描述

2.2.2右移操作符

右移操作符>>移位规则:
1.逻辑右移:左边补0,右边丢弃
2.算术右移:左边补充该值的符号位,右边丢弃

对于正整数而言,正码、反码、补码都是一样的,逻辑右移和算术右移也是一样的,因为他们的最高位都是0,但对于负数而言则不一样,其最高位为1
例如:int num=-1;
在这里插入图片描述
警告:对于移位操作符,没有明确规定右移时是逻辑右移还是算术右移,取决于编译器,在VS下是算术右移
不要移动负数位,这个是标准未定义的
例如:int num=10;
num>>-1;//error

2.3位操作符

&(按位与)(对应二进制位有0则为0,两个同时为1才为1,符号位也一样)
| (按位或)(对应的二进制位有1则为1,两个同时为0才为0,符号位也一样)
^ (按位与或)(对应的二进制位相同为0,不同则为1,符号位也一样)
注:他们的操作数必须是整数,比较的还是操作数的二进制位补码

例子:

int main()
{
    
    
	int num = 1;
	int num1 = 2;
	int a = num & num1;
	int b = num | num1;
	int c = num ^ num1;
	printf("%d %d %d", a, b, c);
	return 0;
}

在这里插入图片描述

2.4赋值操作符

int c = 10;
int x = 0;
c = x =x+1;//连续赋值,这样可读性不高
x = x+1;
c = x;//这样可读性高,易于调试

其次注意的是复合赋值符的用法

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

int x += 2;
x = x+2;

int x -= 2;
x = x-2;

int x *= 2;
x = x*2;
//其他运算符也一样

2.5单目操作符

2.5.1单目操作符介绍

!(逻辑反操作符)
-(负值)
+(正值)
&(取地址)
sizeof (操作数的类型长度(以字节为单位))
~(对一个数的二进制按位取反)
–(前置、后置- -)
++(前置、后置++)

#include <stdio.h>
int main()
{
    
    
	int a = -10;
	int* p = NULL;
	printf("%d\n", !2);
	printf("%d\n", !0);
	a = -a;
	p = &a;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof a);//可以这样写,也说明了sizeof不是函数
	//printf("%d\n",sizeof int);//这样写不支持
	return 0;
}

在这里插入图片描述

2.5.2sizeof和数组

void test1(int arr[])//接收首地址,为了方便表示,可以用int arr[]形式来接收传过来的地址,也可用int* arr来表示,所以arr本质上还是一个指针
{
    
    
	printf("%d\n", sizeof(arr));//求数组名的大小,数组名本质上是首元素地址,地址就是指针,相当于求的就是指针大小,在32平台上指针大小为4个字节,在64位平台上指针大小为8
}
void test2(char ch[])
{
    
    
	printf("%d\n", sizeof(ch));
}
int main()
{
    
    
	int a = -10;
	int* p = NULL;
	int arr[10] = {
    
     0 };
	char ch[10] = {
    
     0 };
	printf("%d\n", sizeof(arr));//求数组的大小
	printf("%d\n", sizeof(ch));//求数组的大小
	test1(arr);//将数组名首地址传过去
	test2(ch);
	return 0;
}

在这里插入图片描述

int main()
{
    
    
	int a = 10;
	int x = ++a;//先对a自增,然后再使用,a和x的值都为11
	int y = --a;//a先自减,然后再使用,a和y的值都为10
	printf("x=%d y=%d\n", x, y);

	int b = 10;
	int m = b++;//先对b使用,即先把b赋值给m,然后b在增加,m=10,b=11
	int n = b--;//现对b使用,即先把b赋值给n,然后b在自减,n=11,b=10
	printf("m=%d n=%d\n", m, n);
	return 0;
}

在这里插入图片描述

2.6关系操作符

>
>=
<
<=
!=  用于测试不相等
== 用于测试相等

注意:在编程过程中注意==和=的使用不恰当导致的错误

2.7逻辑操作符

&& (逻辑与)
|| (逻辑或)

注意:逻辑与(&&)和按位与(&)
1&2------>0
1&&2---------->1
逻辑或(||)和按位与(|)
1|2-------------->3
1||2------------->1
不要将他们混淆了
360笔试题:

int main()
{
    
    
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;//当该表达式1为真时,执行表达式2,当2位真时,执行表达式3,而当表达式1为假时,后面的都不执行,或者1为真,2为假时,3就不执行,
	//i = a++ || ++b || d++;//对于||,只要有一个为真,该表达式为真,若表达式1为真,后面就不执行了,若1为假,2为真,3就不执行了
	printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
	return 0;
}

在这里插入图片描述

2.8条件操作符

exp1?exp2:exp3

if(a>5)
   b=3;
else
   b=-3;
//将该表达式转化为条件表达式
b=a>5?3:-3;//如果表达式1为真,执行表达式2,否则执行表达式3

2.9逗号表达式

exp1,exp2,…expN

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

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

在这里插入图片描述

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

[ ] 下标引用操作符 操作数:一个数组名+一个索引值 如:arr[0];
( ) 函数调用操作符(接受一个或多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数)如:test(1,2)
. 结构体.成员名
-> 结构体指针->成员名

struct stu
{
    
    
	char name[20];
	int age;
	char sex[5];
};
int main()
{
    
    
	struct stu a;
	struct stu* b = &a;
	a.age = 20;//结构体变量访问成员
	b->age = 20;//结构体指针访问成员
	return 0;
}

2.11表达式求值

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

2.11.1隐式类型转换

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

int main()
{
    
    
	char a = 1;
	char b = 2;
	char c;
	c = a + b;//b和c 的值被提升为普通整形,计算完之后,在截断
	return 0;
}

在这里插入图片描述
同理,对于负数而言,在进行整形提升时,二进制补码最高位补充符号位1,运算后的结果被截断。
对于无符号整形提升时,无符号的最高位就不是符号位了,而是有效数字了,此时整形提升时,高位补0,运算完之后在截断。

int main()
{
    
    
	char a = 0xb6;//10110110
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)//等于号左边的a的值为0xb6,由于是char型,发生整形提升,等于号右边的值为无符号整形,两者比较不相等
		printf("a");
	if (b == 0xb600)//同理b也是
		printf("b");
	if (c == 0xb6000000)//c是int类型,不需要发生整形提升,两者比较相等
		printf("c");
	return 0;
}

在这里插入图片描述

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

在这里插入图片描述

这里的%u是打印无符号十进制形式,通过结果可以发现,+c和-c中的c都发生了运算,只要发生运算就会进行整形提升,所以sizeof(+c)和sizeof(-c)都是4个字节大小,而sizeof(c)计算的就是c的本身类型大小大小为1个字节

2.11.2算术转换

long double
double
float
unsigned long int
long int
unsigned int
int
unsigned char
char
当进行以上不同类型的操作数计算时,其转换方式是由下向上转换的,若算术转换从上往下转时,会存在一些精度的丢失

float f=3.14;
int num=f;//隐式转换,会有进度丢失

2.11.3操作符的属性

复杂表达式的求值有三个影响的因素。
1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序
两个相邻操作符先执行哪个取决于他们的优先级,若优先级相同,取决于他们的结合性
在这里插入图片描述
在这里插入图片描述![在这里插入图片描述]直https://接上传(blog.csdnimg.c-/dY075fd7a6HL1dc641ce94233613eaa9.png265)(https:/在这里插入图片描述
一些问题表达式

a*b+c*d+e*f;//*的优先级比+高,所以先算*,但第三个表达式中的*与第一个表达式的*不是相邻的,第三个*与第一个+的执行顺序就产生了歧义,结果可能相同

到底是先算
ab
c
d
ab+cd
ef
a
b+cd+ef
还是
ab
c
d
ef
a
b+cd
a
b+cd+ef

c+--c;
//+的左操作数先准备好还是--c之后准备好c
nt main()
{
    
    
	int i = 10;
	i = i-- - --i * (i = -3) * i++ + ++i;
	//到底是现--i准备好i后,还是++i准备好i后计算呢,不同编译器存在不同的差异
	return 0;
}

end~

猜你喜欢

转载自blog.csdn.net/weixin_68201503/article/details/130901717
今日推荐