玩转C语言系列|【C语言】玩转操作符——操作符的那些东西!
文章目录
- 前言
- 一、C语言中的操作符
- 二、操作符详细介绍
-
- 1.算术操作符
- 2.移位操作符
-
- 2.1移位操作符
-
- 2.1.1左移操作符
- 2.1.2右移操作符
- 3、位操作符
-
- 3.1位操作符的分类
- 3.1.1按位与&
- 3.1.2按位或|
- 3.1.3按位异或^
- 3.1.3使用场景
- 4.赋值操作符
- 5.单目操作符
-
- 5.1操作符&、*
- 5.2 操作符sizeof
- 5.3操作符~
- 5.4操作符++ 、--
- 6.关系操作符
- 6.逻辑操作符
- 7.条件操作符
- 8.逗号表达式
- 9.下标引用、函数调用和结构成员
-
- 9.1下标引用
- 9.2函数调用
- 9.3访问一个结构的成员
- 10.表达式求值
-
- 10.1隐式类型转换
- 10.2 算术转换
- 10.3操作符的属性
- 最后
前言 是否能很好的理解的操作符的意思,决定着我们能否很好的理解代码。在一些相对复杂的程序中,不能搞懂操作符,我们将一筹莫展。因此,本文耗费三天的准备才行文,希望对大家有帮助!
提示:以下是本篇文章正文内容
一、C语言中的操作符 C语言提供了以下的操作符(有的地方也称为运算符)
运算操作符 | + ,-,*,/,%,++,– |
---|---|
关系操作符 | <,>,==,>=,<=,!= |
逻辑运算符 | !,&& |
位运算符 | <<,>>,~,^,& |
赋值运算符 | =及其扩展赋值运算符 |
条件运算符 | ?: |
逗号运算符 | , |
指针运算符 | *,& |
求字节数运算符 | sizeof |
强制类型转换运算符 | (类型) |
成员运算符 | . , -> |
下标运算符 | [ ] |
其他 | 如函数调用运算符() |
二、操作符详细介绍 1.算术操作符 最常用的算术操作符如下:
/%+-*
举例 | 结果 |
---|---|
a+b | a和b的和 |
a-b | a和b的差 |
a%b | a除以b的余数 |
a/b | a除以b的商 |
a*b | a和b的乘积 |
2.移位操作符 2.1移位操作符
- 由于键盘无×号?号,用*和/代替
- 两个实数相除的结果是双精度实数,两个整数相除的结果为整数,如7/3的结果值为2,舍去小数部分。
- %操作符要求参加运算的运算对象(即操作数)为整数,结果也为整数。如7/3,结果为1,返回的是整除之后的余数。
- 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除 法。
1、<< 左移操作符2.1.1左移操作符 我们思考以下代码,printf输出的是什么?也就是在程序中,它是如何移位的?这是移位操作符的重点!
2、>> 右移操作符
int a = 5;
//4byte-32bit=
int b = a << 1;
printf("%d\n", b);
我们知道程序在计算机中是以二进制的方式运行的,所以在这里进行移位的时候,我们也是以二进制的方式来进行移位。
所以:
那么问题来了,原码,反码,补码怎么计算?
- 我们得把a转化为二进制,因为a是int类型,所以是4个字节32个比特位。
- 计算机中,整数的二进制有三种表现形式——原码、反码、补码。
- 对于正整数-原码、反码、补码相同,对于负整数-需要计算
这个也不难。
首先需要明确的是,原码、反码、补码的第一位为符号位,正数符号位为0,负数为1
那么,计算机是如何进行移位的?
- 原码—直接按照数字的正负写出的二进制序列。 比如 -1
原码:000000000000000000000000000000001- 反码—原码的符号位不变,其他位按位取反得到的 所以a的
反码:111111111111111111111111111111110- 补码-反码+1
补码:111111111111111111111111111111111
要想知道这个问题的答案,我们需先知道数字在计算机的存储形式。
计算机存的是补码,我们打印的是原码所以,移位在计算机中是以补码的形式移动的。
下面我们回到例子中
int a = 5;
//4byte-32bit=
int b = a << 1;
printf("%d\n", b);
【如下图】
文章图片
最后将二进制的数字转化成十进制,答案为10.
文章图片
如果我们移动的是负数呢?那么这时候又该如何移动呢?
int c = -1;
int d = c << 1;
printf("%d\n", d);
负数也是同样的原理,先把数字转化成二进制,然后计算出补码,计算机用补码进行移动,移动完成后,再转化成补码打印出来。
下面我们再画一个图给大家演示一下。
文章图片
文章图片
打印出来的结果:
文章图片
2.1.2右移操作符
既然左移是二进制移动的,那么同理,右移也是二进制移动的,这里不再细讲,大家可以按照同样的思路来验证一下下面的右移结果
int a = 5;
int b = a >> 1;
//逻辑右移,右边丢弃,左边补0
//算术右移,右边丢弃,左边补原符号位,原来是正数就正数,负数就负数,绝大多数编译器采用,比如vs2013
printf("%d\n", b);
文章图片
3、位操作符 3.1位操作符的分类
& //按位与3.1.1按位与&
| //按位或
^ //按位异或
注:他们的操作数必须是整数。
【作用】使参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0【例子】
int main()
{ int a = 3;
int b = -2;
int c = a&b;
printf("%d\n", c);
return 0;
}
同样的,这里要先算出a与b的补码。
【如图】
文章图片
文章图片
3.1.2按位或|
【作用】其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。【例子】
int main()
{ int a = 3;
int b = -2;
int c = a|b;
//计算的时候,内存的补码进行计算,所以需写出a与b的二进制
printf("%d\n", c);
return 0;
} //000000000000000000000000000000113的二进制序列的补码
//11111111111111111111111111111110-2的补码
//11111111111111111111111111111111只要有1,就为1,这个为计算完之后内存的补码,回到第一条注释 //11111111111111111111111111111111计算机内存的补码,1开头为负数,需计算原反补码
//11111111111111111111111111111110-1
//10000000000000000000000000000001取反
文章图片
3.1.3按位异或^
【作用】其功能是参与运算的两数各对应的二进位异或,相同为零,相异为1
int main()
{int a = 3;
int b = -2;
int c = a^b;
//二进制位异或
printf("%d\n", c);
//异或-相同为0,相异为1
//11111111111111111111111111111101
//11111111111111111111111111111100
//10000000000000000000000000000011-3
//000000000000000000000000000000113的二进制序列的补码
//11111111111111111111111111111110-2的补码
//11111111111111111111111111111101
return 0;
}
这里不再详解,只需遵循上面所说的技巧与方法便可求解。
文章图片
3.1.3使用场景
1、不创建第三个变量,实现两个数的交换。在这个时候我们便可以使用到上面的操作符了
#include
int main()
{ int a = 39;
int b = 20;
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;
}
文章图片
这里大家有兴趣的话,可以参照上面的方法,讲两个整数转化成二进制进行计算验证一下。
2、求一个整数存储在内存中的二进制中1的个数。
#include
int main()
{ int a= 2;
int i = 0;
int count = 0;
//计数
for(i=0;
i<32;
i++)
{if( a& (1 << i) )
count++;
}
printf("二进制中1的个数 = %d\n",count);
return 0;
}
文章图片
4.赋值操作符 1、赋值符号“=”就是赋值操作符
【作用】将一个数据赋给一个变量【例子】
int a=3;
//把常量3赋给变量a
2、复合的赋值操作符
+=比如:
-=
*=
/=
%= >>=
<<=
&=
|=
^=
a+=3 等价于 a=a+35.单目操作符
x*=y+8 等价于 x=x*(y+8)
x%=3 等价于 x=x%3
! 逻辑反操作,!a ——含义:如果a为假,则!a为真,如果a为真,则非!a为假【注意】在C的逻辑运算中,以“1”代表“真”,以“0”代表“假”
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
– 前置、后置–
++ 前置、后置++
* 间接访问操作符(解引用操作符) (类型) 强制类型转换
下面重点讲解一下&、*、sizeof、~、++、–
5.1操作符&、*
取地址(&)操作符与解引用操作符(*)放一块将讲
取地址(&)
1、&后面是个变量。简单来说——&是取地址运算符,&a为变量a的地址。
每个变量对应一块存储空间。每个存储空间有一个编号,也就是地址。
&变量名 , 表示取出这个编,变量名表示取出这个编号所对应的存储空间里的值。
解引用操作符(*)
提取一个变量的地址。&就提取它的地址,由地址找到a在内存中的空间。*是指针运算符。举个例子:
int main()
{ int a = 10;
int *p = &a;
//取地址由*p接受,*p为指针变量,p的类型为 int*,
//取地址是由低到高取,也就是取首地址
intb = *p;
//右值描述的是空间的内容
//*p赋给b,并通过*p的值找到a,把a的值赋给b,当这里写*p的时候,找到的是a里面的值,用的是a空间的值,也就是找到10
//这里因为用的是a的空间,所以下面是赋值给a
*p=20;
//*为解引用操作符,*p就是同p里面存的地址,找到它所指的对象
printf("%d\n", a);
return 0;
}
文章图片
5.2 操作符sizeof
sizeof() 是一个判断数据类型或者表达式长度的运算符
int main()
{ int a = 10;
printf("%d\n", sizeof(a));
//4
printf("%d\n", sizeof a);
//可以省略括号,说明不是函数
printf("%d\n", sizeof(int));
//4.为什么?int的类型用来创建a的,return 0;
}
文章图片
5.3操作符~
~表示按位取反,这个也是需要转换成二进制补码的
int main()
{ int a = 0;
//0000000000000000000000000000000
int b = ~a;
//按位取反
//0000000000000000000000000000000
//1111111111111111111111111111111
//1111111111111111111111111111110
//1000000000000000000000000000001
//-1
printf("%d\n", b);
return 0 ;
}
文章图片
5.4操作符++ 、–
int main()
{ int a = 10;
printf("a=%d\n", a);
int b1 = ++a;
//前置++,先++,再使用
int b2 = --a;
//前置--,先--,再使用
int b3= a++;
//后置++,先使用再++
int b4 = a--;
//后置--,先使用后--
printf("b1=%d\n", b1);
printf("b2=%d\n", b2);
printf("b3=%d\n", b3);
printf("b4=%d\n", b4);
printf("a=%d\n", a);
return 0;
}
文章图片
6.关系操作符
=关系运算符跟我们平时在数学上的意思差不多,这里值得注意的是==,千万注意,在写的时候不要漏掉!
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
6.逻辑操作符
&& 逻辑与【注意】这里要区分逻辑与和按位与,逻辑或和按位或
|| 逻辑或
也就是
&与&&
|与||
运算符 | 说明 |
---|---|
逻辑与(&&) | 如果a和b都为真,则结果为真,否则为假 |
逻辑或 | 如果a和b有一个以上为真,则结果为真,二者都为假时,结果为假 |
【区别】逻辑与和逻辑或,是用来判断的 按位与和按位或,是用来计算的
int main()
{ int a = 3;
int b = 5;
//int c = a&&b;
//逻辑与,只关注真假
int c = a || b;
//逻辑或
printf("c=%d\n", c);
return 0;
}
文章图片
7.条件操作符 条件操作符的一般形式
表达式1?表达式2:表达式3【说明】条件运算符由两个符号(?和:)组成,必须一起使用。要求有3个操作对象,称为三目(元)运算符,它是C语言中唯一的一个三目运算符。
那么,这个操作符表达的是什么意思?
我们借助一个例子,求两数中的最大值
if(a>b)
max=a;
else
max=b;
以上代码可以改成
max=(a>b)?a:b;
意思是:
如果(a>b)的条件为真,则表达式的值等于a;否则取值b。8.逗号表达式 一般形式:
(表达式1,表达式2,表达式3,… ,表达式n)
(1) 逗号表达式的运算过程为:从左往右逐个计算表达式。如:(3+5,6+8)称为逗号表达式,其求解过程先表达式1,后表达式2,整个表达式值是表达式2的值。(3+5,7+8)的值是15;
(2) 逗号表达式作为一个整体,它的值为最后一个表达式(也即表达式n)的值。
(3) 逗号运算符的优先级别在所有运算符中最低。
int main()
{ int a = 0;
int b = 0;
int c = 0;
int d=(a = 3,b = 5,b += a,c = b * 5);
printf("%d\n", d);
return 0;
}
文章图片
9.下标引用、函数调用和结构成员 9.1下标引用
操作数:一个数组名 + 一个索引值
int main()
{ int arr[] = {
1, 2, 3, 4, 5 };
int i = 0;
printf("%d\n", arr[4]);
//通过下标,寻找arr数组的元素
return 0;
}
文章图片
9.2函数调用
( ) 函数调用操作符
void test()
{ printf("hehe\n");
}
int main()
{ test();
//不传参,也叫函数调用操作符
return 0;
}
文章图片
9.3访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
#include
#include
//声明类型
struct Book;
//自定义类型
struct Book
{ char name[20];
float price;
char id[10];
};
void print1(struct Book b)
{ printf("书名:%s\n", b.name);
printf("价格:%f\n", b.price);
printf("书号:%f\n", b.id);
}void print2(struct Book* pb)
{ /*printf("书名:%s\n", (*pb).name);
printf("价格:%f\n", (*pb).price);
printf("书号:%s\n", (*pb).id);
*/
//上下二者等价
printf("书名:%s\n", pb->name);
printf("价格:%f\n", pb->price);
printf("书号:%s\n", pb->id);
}int main()
{ structBook b = {
"C语言程序设计",55.5f,"C20190203"};
print2(&b);
//print1(b);
//b.price = 100.0f;
字符串拷贝-strcpy-库函数
//strcpy(b.name, "数据结构");
//print1(b);
//结构成员访问操作符
//.左边是结构体变量,右边是成员们结构变量.成员名
//->结构体指针->成员名
//(*结构体指针).成员名
return 0;
}
文章图片
10.表达式求值 10.1隐式类型转换
整型提升
整型提升是C程序设计语言中的一项规定:在表达式计算时,各种整形首先要提升为int类型,如果int类型不足以表示则要提升为unsigned int类型;然后执行表达式的运算。举个例子
int main()
{ char a = 3;
//a是一个字节,只有8bit位
//0000000000000000000000000000000000113的原码反码补码
//00000011 --a里面存的东西,也就是将最后面的8位截取下来
char b = 127;
//00000000000000000000000000001111111127的原码反码补码
//01111111--b里面的东西,也就是将最后面的8位截取下来 //a和b自身都是char类型,自身大小都是一个字节,所以这里计算的时候要进行整型提升
//
//00000000000000000000000000000011
//00000000000000000000000001111111
//00000000000000000000000010000010 char c = a + b;
//放回c中的时候,同样截断,10000010
//高位是1,为负数
//111111111111111111111111111110000010
//100000000000000000000000000001111110
printf("%d\n", c);
return 0;
}
文章图片
【整型提升的意义】
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU(general-purpose
CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned
int,然后才能送入CPU去执行运算。
——摘自百度百科(整型提升)
10.2 算术转换
当程序运行时遇到不同类型的数据进行运算,先将二者自动转换成同一种类型,再进行运算
float a = 10.0;
double b = 9.5;
在这里,如果进行+、-、*、/,运算的时候,系统会将所有的float型数据都先转化成double,然后进行运算。
这个转换时编译系统自动完成,用户不必过问。
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
警告:
但是算术转换要合理,要不然会有一些潜在的问题.
例如:
float f = 3.14;
int num = f;
//隐式转换,会有精度丢失
10.3操作符的属性
复杂表达式的求值有三个影响的因素。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序。
操作符优先级(下图来源于网络)
文章图片
最后 最近学习C语言有点累了,时常感觉前路迷惘,害怕自己不是这块料却非要往这里凑。本文撰述了将近6个小时。参考了谭浩强《C语言设计》(第五版),以及网上的部分资料,加之自己在学习听课时的笔记,拼拼凑凑而成,尽管如此,却也花费了我很多心思。 【玩转C语言系列|【C语言】玩转操作符——操作符的那些东西!】希望能对看到的大家有所帮助!
推荐阅读
- 【生信技能树】R语言练习题|【生信技能树】R语言练习题 - 中级
- 【欢喜是你·三宅系列①】⑶
- 一起来学习C语言的字符串转换函数
- C语言字符函数中的isalnum()和iscntrl()你都知道吗
- C语言浮点函数中的modf和fmod详解
- C语言中的时间函数clock()和time()你都了解吗
- 你不可不知的真相系列之科学
- 人脸识别|【人脸识别系列】| 实现自动化妆
- C语言学习|第十一届蓝桥杯省赛 大学B组 C/C++ 第一场
- 2018-06-13金句系列7(金句结构-改编古现代诗词)