[C语言学习]----操作符详解

本篇详细介绍了C语言的操作符

[C语言学习]----操作符详解_第1张图片

 

程序的艺术来源于生活

目录

1.算术操作符

2.移位操作符

2.1左移操作符

2.2右移操作符

3.位操作符

3.1位操作符面试题

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

 3.1.2求一个整数存储在内存中的二进制中1的个数

 4.赋值操作符

4.1复合赋值符

5.单目操作符

6.关系操作符

7.逻辑操作符

8. 条件操作符(三目操作符)

9.逗号表达式

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

10.1[ ] 下标引用操作符

10.2( )函数调用操作符

10.3访问一个结构的成员

11.表达式求值

11.1隐式类型转换

11.2算术转换

11.3操作符的属性


1.算术操作符

+        -        *        /        %

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


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


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

 注:除法要想得到小数的结果,必须保证除数和被除数中至少有一个是小数(浮点数)

5 / 2 = 2;
5 % 2 = 1;//5 % 2 = 2...1
5.0 / 2 = 2.5;
5 / 2.0 = 2.5;

2.移位操作符
 

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

移位操作符,移动的是二进制

例: 数值 15

15是十进制表示法

[C语言学习]----操作符详解_第2张图片

 15的二进制是1111

[C语言学习]----操作符详解_第3张图片

整形的二进制表示:
原码反码补码

符号位是0,表示正数

符号位是1,表示负数

正整数的原码,反码,补码相同

例:

在C语言中可以存放在int类型的变量中,int类型是4个字节,也就是32个bit位

15 - 整数

00000000 00000000 00000000 00001111-源码

00000000 00000000 00000000 00001111-反码

00000000 00000000 00000000 00001111-补码

-15 

10000000 00000000 00000000 00001111-源码

11111111 11111111 11111111 11110000    -反码(源码符号位不变,其他位按位取反

11111111 11111111 11111111 11110001    -补码(反码+1

 

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

移位操作符,移动的是存储在内存中的补码

2.1左移操作符

移位规则:


左边抛弃、右边补0

正数: 

#include
int main()
{
	int a = 4;
	// 00000000 00000000 00000000 00000100
	int b = a << 1;//把a向左移动一位
	printf("%d %d",a,b);
	return 0;
}

[C语言学习]----操作符详解_第4张图片

 [C语言学习]----操作符详解_第5张图片

 负数:

#include
int main()
{
	int a = -4;
	// 11111111 11111111 11111111 11111100    -4的补码
	int b = a << 1;
     //a向左移动1位
	printf("%d %d",a,b);
	return 0;
}

[C语言学习]----操作符详解_第6张图片
 

[C语言学习]----操作符详解_第7张图片

左移1位相当于原数*2


2.2右移操作符

移位规则:
 

首先右移运算分两种:

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


2. 算术移位(绝大部分编译器用算术移位
左边用原该值的符号位填充,右边丢弃

正数:

#include
int main()
{
	int a = 4;
	// 00000000 00000000 00000000 00000100
	int b = a >> 1;	    //a向右移动1位
	printf("%d %d",a,b);
	return 0;
}

[C语言学习]----操作符详解_第8张图片

 负数:

#include
int main()
{
	int a = -4;
	// 11111111 11111111 11111111 11111100    -4的补码
	int b = a >> 1;	    //a向右移动1位
	printf("%d %d",a,b);
	return 0;
}

[C语言学习]----操作符详解_第9张图片

[C语言学习]----操作符详解_第10张图片

右移1位相当于原数/2

注:

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

例:

int num = 10;
num>>-1;//error

3.位操作符

& //按位与

//按位或

^ //按位异或

注:它们的操作数必须是整数

位操作符都是二进制来进行操作的

& 按位与,有0则0,全1为1

#include
int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;//& - 按(2进制)位与
	printf("%d\n", c);

	//
	//00000000000000000000000000000011 - 3的补码
	//10000000000000000000000000000101 -5的原码
	//11111111111111111111111111111010 -5的反码
	//11111111111111111111111111111011 -5的补码

	//00000000000000000000000000000011 - 3 & -5
	return 0;
}

| 按位或,有1则1,全0为0

#include
int main()
{
	int a = 3;
	int b = -5;
	int c = a | b;//& - 按(2进制)位或
	printf("%d\n", c);

	//
	//00000000000000000000000000000011 -> 3的补码
	//10000000000000000000000000000101 -5的原码
	//11111111111111111111111111111010 -5的反码
	//11111111111111111111111111111011 -5的补码
	//
	//11111111111111111111111111111011 c的补码
	//11111111111111111111111111111010 c的反码
	//10000000000000000000000000000101 -> -5(c的原码)
	return 0;
}

^ 按位异或,相同为0,相异为1

#include
int main()
{
	int a = 3;
	int b = -5;
	int c = a ^ b;//& - 按(2进制)位异或
	printf("%d\n", c);
	//异或的运算:相同为0,相异为1
	//
	//00000000000000000000000000000011 -> 3的补码
	//10000000000000000000000000000101 -5的原码
	//11111111111111111111111111111010 -5的反码
	//11111111111111111111111111111011 -5的补码
	//
	//11111111111111111111111111111000 c的补码
	//11111111111111111111111111110111 c的反码
	//10000000000000000000000000001000 c的原码
	//-8
	//
	return 0;
}

3.1位操作符面试题

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

观察下面的运算得出

3^3 = 0
5^5 = 0
3^5 = 6
3^5^5 = 3
3^5^3 = 5

两个相同的数字异或结果为0,且异或是支持交换律

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

	a = a ^ b;
	b = a ^ b;//b=a ^ b ^ b
	a = a ^ b;//a^b^a


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

	return 0;
}

[C语言学习]----操作符详解_第11张图片

 3.1.2求一个整数存储在内存中的二进制中1的个数

我们可以让这个数 & 1,然后再将这个数向右移动1位,因为整数占32个bit位,所以需要循环32次

例:2的二进制表示为 00000000 00000000 00000000 00000010

[C语言学习]----操作符详解_第12张图片 代码实现:

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

 4.赋值操作符

=

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

赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值,从右往左依次赋值

那同样的语义,你看看:
x = y+1;
a = x;
这样的写法是不是更加清晰爽朗而且易于调试

4.1复合赋值符

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

这些运算符都可以写成复合的效果
 

int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁。

5.单目操作符

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

是将真变成假,假变成真,进行逻辑反操作

通常用于条件判断,当某个条件为假,去执行程序

#include
int main()
{
	int flag = 0;
	if (!flag)
	//if(flag == 0)//理论上可以,逻辑不一样  !flag是真假的感念
   //                                      flag == 0是判断为0的概念   
	{
		printf("hehe\n");
	}

	return 0;
}

 

+ -

表示数字的正负


 

&  * 

&  拿到数据在内存中的地址

*  解引用操作,通过地址得到地址中存放的数据

数据在内存中存储都会有其相对应的地址,通过&操作,我们可以将地址赋值给指针变量,方便我们找到这块空间

#include
int main()
{
    int a = 4;
    int* pa = &a; //&a是指针,pa是指针变量,pa的类型是int*,pa存放a的地址
    return 0;
}

想通过pa找到a,需要用到 * (解引用)

可以通过解引用修改a的值

#include
int main()
{
	int a = 4;

	int* pa = &a;//& - 取地址操作符
	//*pa = 20;//* - 解引用操作符
	*pa = 20;
	printf("%d",a);

	return 0;
}

 

注:

&数组名,是取出数组的整个地址


sizeof  用来计算类型大小,单位是字节

#include
int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));//4
	printf("%d\n", sizeof a );//4  在计算变量的大小时候可以去掉(),但是()最好写上 
	printf("%d\n", sizeof(int));//
	printf("%d\n", sizeof int);//err,但是在计算数据类型时,()不能去掉 
	return 0;
}

 通过sizeof可以计算数组元素个数


#include
int main()
{
	int arr[10] = { 0 };
	printf("%d\n", sizeof(arr));//40,计算的是数组的总大小,单位是字节
	printf("%d\n", sizeof(arr[0]));//4
	printf("%d\n", sizeof(arr) / sizeof(arr[0]));//40 /4=10
 
	//数组的元素个数如何计算
	int sz = sizeof(arr) / sizeof(arr[0]);
	return 0;
}

sizeof()内部放的表达式是不计算的

 

#include
int main()
{
	short s = 10;
	int a = 2;
	printf("%zu\n", sizeof(s = a + 5));
	
	printf("%d\n", s);
	return 0;
}

[C语言学习]----操作符详解_第13张图片 

通过运行我们发现,sizeof只会查看()内是什么形式,但不会真正的去计算,所以s的值不发生变化

原理:在编译期间就把sizeof()计算出来,并替换成计算出来的值,所以在程序执行时,就不会发生表达式的计算

 

strlen和sizeof的对比:单目操作符

数组名是数组首元素地址(有两个例外)

1.sizeof(数组名),计算整个数组大小,sizeof内部单放一个数组名,数组名表示整个数组

2.&数组名,取出整个数组的地址


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

#include
int main()
{
	int a = 0;
	printf("%d",~a);   //输出值为-1
	return 0;
}

 原理:

[C语言学习]----操作符详解_第14张图片


前置++(--)先进行++(--)操作,再使用

int main()
{
	int a = 10;
	int b = ++a;//前置++,先++,后使用
	printf("a=%d b=%d\n", a, b);
    return 0;
}

[C语言学习]----操作符详解_第15张图片

后置++(--)先使用,再++(--) 

#include
int main()
{
	int a = 10;
	int b = a++;//后置++,先使用,后++
	printf("a=%d b=%d\n", a, b);
 
	return 0;
}

[C语言学习]----操作符详解_第16张图片

 


(类型)强制类型转换

#include
int main()
{
	//int   double
	int a = (int)3.14;//强制类型转换,把double转换成int形
	printf("%d\n", a);
 
	return 0;
}

6.关系操作符

>

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

注:=为赋值,==为判断

在判断时,可以将常量放在 == 左边,防止将  ==  用成  =

7.逻辑操作符

&&逻辑与    (并且)
 ||  逻辑或  (或者)

&&逻辑与 同真为真,有假为假
 ||  逻辑或 有真为真,同假为假

8. 条件操作符(三目操作符)
 

exp1 ? exp2 : exp3

如果exp1为,exp2表达式的结果是整个表达式的结果

如果exp1为,exp3表达式的结果是整个表达式的结果

#include
int main()
{
	int m, a, b;
	scanf("%d %d",&a,&b);
	m = (a > b ? a : b);//利用三目操作符比较两个数的大小
	
	printf("%d\n", m);
 
	return 0;
}

9.逗号表达式
 

exp1,exp2,exp3,...... expn

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

 

#include
int main()
{
	int a = 3;
	int b = 0;
	int c = 4;
 
	int d = (a = b - 3, b = a + c, c = a - b, a = c + 3);//从左到右依次计算,取最后一个表达式的结果
			//a=-3 b=1   c=-4  a=-1
 
	printf("%d\n", d);
	return 0;
}

[C语言学习]----操作符详解_第17张图片

 

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

10.1[ ] 下标引用操作符


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

int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
[]的两个操作数是arr和9

10.2( )函数调用操作符

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

#include 
void test1()
{
    printf("hehe\n");
}
void test2(const char *str)
{
    printf("%s\n", str);
}
int main()
{
    test1(); //实用()作为函数调用操作符。
    test2("hello world");//实用()作为函数调用操作符。
    return 0;
}

10.3访问一个结构的成员

.     结构体.成员名

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

#include 
struct Book
{
	char name[20];
	int price;
};

int main()
{
	struct Book sb = {"C语言", 55};
	
	printf("%s %d\n", sb.name, sb.price);//结构体变量.结构体成员名

	struct Book* ps = &sb;
	printf("%s %d\n", (*ps).name, (*ps).price);
	printf("%s %d\n", ps->name, ps->price);//结构体指针->结构体成员名

	return 0;
}

[C语言学习]----操作符详解_第18张图片

11.表达式求值

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


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

11.1隐式类型转换


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


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

如何进行整形提升:

整形提升是按照变量的数据类型的符号位来提升的

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111


//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001


//无符号整形提升,高位补0

例:
 

char a,b,c;
...
a = b + c;

b和c的值被提升为普通整型(int),然后再执行加法运算

加法运算完成之后,结果将被截断,然后再存储于a中

#include
int main()
{
	char a = 5;//截断
	char b = 126;//截断
	char c = a + b;//截断
	//00000000000000000000000000000101
	//00000101 - a
	//00000000000000000000000001111110
	//01111110 - b
	//整型提升
	//00000000000000000000000000000101-a
	//00000000000000000000000001111110-b
	//00000000000000000000000010000011
	//10000011 - c
	printf("%d\n", c);//打印有符号整数,第一位为1,是负数
	//%d 十进制的方式打印有符号整数
	//11111111111111111111111110000011 整形提升为int-补码
	//11111111111111111111111110000010              反码
	//10000000000000000000000001111101              原码
	//结果为-125
	//
	return 0;
}

 实例1:

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

实例1中的a,b要进行整形提升,但是c不需要整形提升


a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真.

[C语言学习]----操作符详解_第19张图片

 

11.2算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。

下面的层次体系称为寻常算术转换

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

如果某两个操作数的类型是上面的类型

排名较低的操作数类型会转变为排名较高的操作数类型

注:但是算术转换要合理,要不然会有一些潜在的问题

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

11.3操作符的属性


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

1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。

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


操作符优先级:

优先级 操作符 描述 结合方向
1

()

[]

.

->

圆括号

下标引用

访问结构成员

访问结构指针成员

左->右
2

~

+

-

++

--

*

&

sizeof

(类型)

逻辑反

按位取反

正值

负值

自增

自减

间接访问(解引用)

取地址

求大小,单位字节

类型转换

右->左
3

+

-

*

%

/

加法

减法

乘法

取余

除法

左->右
4

<<

>>

左移位

右移位

左->右
5

>

>=

<

<=

==

!=

大于

大于等于

小于

小于等于

等于

不等于

左->右
6

&

|

^

按位与

按位或

按位异或

左->右
7

&&

||

逻辑与

逻辑或

左->右
8 ? : 条件操作符(三目操作符) 左->右
9 = 赋值 右->左
10

+=

-=

*=

/=

%=

加等

减等

乘等

除等

取余等

右->左
11

<<=

>>=

左移等

右移等

右->左
12

&=

|=

^=

按位与等

按位或等

按位异或等

右->左
13 , 逗号 左->右

 

一些问题表达式:

//表达式的求值部分由操作符的优先级决定。
//表达式1
a*b + c*d + e*f

表达式1代码在计算的时候,由于*比+的优先级高,只能保证,*的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行

 

//表达式2
c + --c;

操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义

结语:本篇是C语言操作符的知识,如果本篇对你有用,请大家点赞关注!!!

现在关注是新粉,以后就是老粉了

 

 感谢你的观看,我们下期再见

 

你可能感兴趣的:(C语言,学习,c语言)