初学C语言:运算符,一个也不放过
前言本人为C语言初学者,学识尚浅,研究程度存在很大的局限性,眼界很窄。以下所有观点仅代表个人见解和思路,各位游刃有余的前辈可以给予批评和指正!各位与鄙人同路的学子可相互探讨、发表看法,交换观点!
目录
优先级 1
1.[] - 数组下标操作符
2.() - 圆括号
3.-> - 成员选择(指针)和 . - 成员选择(对象)
4.++ - 自增运算符和 -- - 自减运算符
优先级 2
1. - - 负号运算符
2.(类型) - 强制类型转换
3. - 取值操作符和 & - 取地址操作符
4.! - 逻辑非操作符
5. ~ - 按位取反操作符
6.sizof操作符
优先级 3
/ - 除 - 乘 % - 取模
优先级 4
+ - 加 - - 减
优先级 5
1.<< - 左移操作符
2.>> - 右移操作符
优先级6
优先级 7
优先级 8
& - 按位与
优先级 9
^ - 按位异或
优先级 10
| - 按位或
优先级 11
&& - 逻辑与
优先级 12
|| - 逻辑或
优先级 13?: - 条件运算符
优先级 14
优先级 15
,- 逗号表达式
扩展整型提升
,我们需要看一看这张大表
这里基本包含了C语言中所有的操作符以及其优先级和结合性的排序,我将以优先级为序来逐个剖析一下他们的具体用法
(,在这之前,我们还是先了解一下什么是目即操作数,单目、双目,也就是操作数的个数而已,很好理解)
优先级 1
1.[] - 数组下标操作符
可以用来访问数组元素,例如以下代码
int arr[5] = {1, 2, 3, 4, 5}; int a = 0; a = arr[0]; a = arr[1]; a = arr[2]; a = arr[3]; a = arr[4];
这里要注意两点(1)数组的下标从0开始索引 (2)数组下标操作符需要两个操作对象数组名和下标
2.() - 圆括号
其实这里的圆括号有两层含义,一是可以包含表达式,提升表达式的优先级,其实和数学中括号内先算的法则一样;二是作为函数的参数列表,这不属于运算符范围内。
#includeint ADD(int x, int y) { return x + y; } int main() { int a = 1, b = 2, c = 3; int result = 0; result = a + b c; //1 result = (a + b) c; //2 result = ADD(a, c); //3 return 0; }
在1中,可知的优先级大于+,所以bc先运算,在2中,括号的优先级最高,所以a+b先运算,括号更像是一种提升优先级的方法。,在3中,它的作用是放函数参数,参数可以没有,括号却必须要赖在这儿不走。
3.-> - 成员选择(指针)和 . - 成员选择(对象)
这里需要大伙儿掌握结构体的知识,这里我只展示使用方法
struct BOOK { char name[20]; char riter[20]; float price; }; struct BOOK a = { "《圆圈正义》", "罗翔", 46.5 }; printf("书名%sn作者%sn定价%.2fn", a.name, a.riter, a.price);
结构体是自己定义的一种复杂类型,比如这本书需要有字符串类型和浮点型,如果我还需要用到更多类型,那么一个个的定义命名和使用就变得非常繁琐,结构体更像是一种集合,方便了许多!
既然作为结构体,需要储存数据,那么在计算机内肯定要开辟一段空间给它,既然这样,它就会有地址,那么是不是可以和char、int之类的类型一样用指针变量存放地址呢?结果是必然的,结构体是什么类型?是......结构体类型!所以,我们也可以向下面这样使用它们
struct BOOK { char name[20]; char riter[20]; float price; }; struct BOOK a = { "《圆圈正义》", "罗翔", 46.5 }; struct BOOK p = &a; printf("书名%sn作者%sn定价%.2fn", (p).name, (p).riter, (p).price); //1 printf("书名%sn作者%sn定价%.2fn", p->name, p->riter, p->price); //2
在第一种写法中,只是简单地将p替换了a而已,道理同前;第二种写法,是地址直接指向对象,用到了->这个操作符。
4.++ - 自增运算符和 -- - 自减运算符
如其名般,就是让自己增加1或者让自己减少1嘛,这里需要区分一下前置自增和后置自增,比如++a和a++
int a = 1, b = 0; b = ++a; a = 1; b = a++;
++a,也就是前置++,b的结果是2,这里a是先着急自增了之后才赋值给b;而a++,也就是后置++,b的结果是1,这里a是先把值给了b才自增。
(后置++优先级比前置++高哦!!!)
优先级 2
1. - - 负号运算符
即将正数变为负数负数变为正数,操作数只有一个
int a = 1; a = -a; //a的值为 -1
2.(类型) - 强制类型转换
如果我们在一个变量或者数的前面加上(类型),就可以强制转换类型,很多时候是为了必要的时候防止警告,又或者对time函数强制转换成unsigned int获得大于0的整数等......
char a = 10; int b = 20; int result = 0; result = (int)a + b; srand((unsigned int)time(NULL));
3. - 取值操作符和 & - 取地址操作符
这里主要涉及到了指针的一些知识,如果我们想要取出一个地址中的值,那么我们往往用到
int a = 1; int pa = &a; printf("a = %d", pa);
,这里的定义指针变量的和打印时取值的这个意义大不相同,前一个只是说明这是一个指针变量,存放的地址,用于定义指针变量;而后一个是取出pa地址中的值供printf函数使用。
& - 取地址操作符同理,pa是指针变量,需要存放一个地址,所以我们把a的地址取出来放到pa中可供间接访问,所以它还有一个名字间接访问操作符。
4.! - 逻辑非操作符
! 可以将真变为假,假变为真,,这里我们要清楚真假概念0为假,非0为真,所以对任意一个非零数使用逻辑非,都会变成0,而对于0使用,只会变成1。
int a = 1; if(a) { printf("hahahan"); } if(!a) { printf("dadadan"); }
屏幕将会打印“hahaha”。
5. ~ - 按位取反操作符
这里是指对该数的二进制位进行按位取反,,也包括符号位!
char a = 13; a = ~a; printf("a = %dn", a); return 0;
我们对此进行分析
a = 13 = 00001101
~a = 11110010
而负数在内存中以补码形式存放,我们想知道它的原码,就得 -1 然后按位取反(这里的按位取反不包括符号位了哦!一定要注意!)
11110010 - 1 = 11110001
~ = 10001110 = - 14
,我脑洞打开,如果把char换成unsigned char会发生什么?同样的代码,让我们看看结果
那242又是哪里来的?我们再来分析一下
a = 13 = 00001101
~a = 11110010 = 242(无符号)
乍一想挺简单的,实际上还是暗藏了一点玄机,详见文末,扩展1。
6.sizof操作符
计算该类型所占空间大小,可以写成以下几种形式
int a = 1; sizeof(a); sizeof a; sizeof(int);
sizeof int;
这样写是错的哦!以防万一,可以在任何时候都带上括号。
优先级 3
/ - 除 - 乘 % - 取模
对于这类运算符相信大家已经是滚瓜烂熟,不需要过多的赘述,我只提一下一些需要注意的小点
(1)不要做出除以0,对小数取模等谜之操作
(2)注意操作数的类型和用于接收值的变量的类型
优先级 4
+ - 加 - - 减
略
优先级 5
1.<< - 左移操作符
这里的操作仍然是针对二进制进行的,例如
int a = 4; a = a << 1;
结果是8,过程如下
a = 4 = (前24位0省略) 00000100
a << 1即把二进制位向左移动一位
多出来的一位被舍弃,而右边空出来的一位自动填充0,所以a就变成了
(前24位0省略)00001000 = 8
2.>> - 右移操作符
和左移操作符类似,却也有不同之处哦!不可大意
int a = 4; a = a >> 1;
一口咬定,答案就是2,道理同上,我还是画个图
如果我换成如下代码
int a = -1; a = a >> 1;
这时候结果会变成什么呢?
结果还是 -1?
这里不得不提到两种填充方式,对于右移操作符,右边多出来的肯定会舍弃,左边空出来的位置到底该填啥?1.算术右移,填充符号位 2.逻辑右移,填充0
所以我们刚刚在VS中计算还是-1,是因为VS是算术位移,就算移了一格,舍弃一个1又填充一个1,所以还是-1,如果填充的是0,那么结果将大相径庭。
优先级6
真不是我懒,而是真的没啥说!
优先级 7
这些运算符得到的结果都是真或者假,需要明晰!
优先级 8
& - 按位与
操作的对象仍然是整型的二进制形式,按位与是将两个整型的二进制进行比较,同真为真,一假则假,即如下
int a = 123; int b = 69; int c = a & b;
c = 65;
计算如下
a = 123 = (前省)0111 1011
b = 69 = (前省)0100 0101
c = (前省)0 1 0 0 0 0 0 1(有0就为0,都是1才是1)
优先级 9
^ - 按位异或
其意思是,如果二进制位相同则为0,不同则为1,我们还是以上面的代码举例
int a = 123; int b = 69; int c = a ^ b;
计算过程如下
a = 123 = (前省)0111 1011
b = 69 = (前省)0100 0101
c = (前省)0 0 1 1 1 1 1 0(相同为0,相异为1)
优先级 10
| - 按位或
对应的二进制位只要有1就是1,两个都是0才为0
int a = 123; int b = 69; int c = a | b;
计算过程如下
a = 123 = (前省)0111 1011
b = 69 = (前省)0100 0101
c = (前省)0 1 1 1 1 1 1 1(有真则真,同假则假)
优先级 11
&& - 逻辑与
两边的表达式为真则为真,有假则为假,且逻辑与如果检测到假就直接返回假,不会判断后面的内容!
int ret = 0; ret = 1 && 1; //1 ret = 0 && 1; //2 ret = 0 && 0; //3
1同真为真,ret = 1;
2有假则假,ret = 0;
3同假为假,ret = 0;
如果代码是这样的
int a = 1; int ret = 0; ret = 0 && ++a;
运行后a的值还是1,因为有0的时候就直接返回了,++a被忽略了。
优先级 12
|| - 逻辑或
有真则真,全假则假,道理和逻辑与基本是相同的。
int ret = 0; ret = 1 || 1; //1 ret = 1 || 0; //2 ret = 0 || 0; //3
1全真为真,ret = 1;
2有真则真,ret = 1;
3全假则假,ret = 0;
如果代码如下
int a = 1; int ret = 0; ret = 1 || ++a;
道理也是一样的,在检索到1时就已经返回了1,++a不被执行。
优先级 13
?: - 条件运算符
int a = 3;
int b = 0;
b = a > 5 ? 1 : -1;
变量 = 表达式1 ? 表达式2 表达式3
表达式1为真,把表达式2的值给变量,不然把表达式3的值给变量
其实这和if语句是完全等价的,见如下语句,和以上语句一毛一样
if (a > 5) { b = 1; } else { b = -1; }
如果你用if语句来理解的话,就简单多了!
优先级 14
所有的运算符其实都是一种简化
例如
a = a + 2 -> a += 2;
a = a << 1; -> a <<= 1;
故等价转换即可,略。
优先级 15
,- 逗号表达式
如果你看见这样的东西
(表达式1, 表达式2, 表达式3);
那么整个逗号表达式的值等于一个表达式的值,见如下代码
int a = 0; a = (++a, ++a, 2 3);
的a应该等于6。
逗号表达式从左至右计算,所以a先+1再+1,但被赋值为6
扩展整型提升
#define _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
//表达式的整型运算一般在CPU内的整型运算器(ALU)中执行,而其中的操作数的字节长度一般是int的字节长度
//也是CPU的通用寄存器的长度
//故如果两个空间不满int类型的整数相加,就会发生整型提升,即先转换成int或unsigned int类型再计算
//转换方式有符号则以符号位填充,无符号则填充0
char a = 127; //01111111
char b = 3; //00000011
char c = a + b;
//真实的相加是这样的00000000 00000000 00000000 01111111 + 00000000 00000000 00000000 00000011
//= 00000000 00000000 00000000 10000010
//然后被截断 c = 10000010
//符号位是1,所以原码是补码 - 1 然后按位取反 = 11111110 = -126
printf("c = %dn", c);
//在printf时,c是char类型,是以整型输出,则发生整型提升
//c = 111111110 -> 11111111 11111111 11111111 10000010
//然后 -1 按位取反 -> 11111111 11111111 11111111 10000001 ->10000000 00000000 00000000 01111110
//所以c输出的是 -126
//输出和计算是不同的过程,经历两次整型提升,不要搞混
//例1
char i = 0xb6;
short j = 0xb600;
int k = 0xb6000000;
if (i == 0xb6)
printf("in");
if (j == 0xb600)
printf("jn");
if (k == 0xb6000000)
printf("kn");
//例2
char x = 1;
char y = 1;
int d = 10, e = 5;
printf("%un", sizeof(+x)); //sizeof检索到x被整型提升后是int类型,所以输出了4(如果是!x,输出是1,实际上是错误的,g中是4)
printf("%un", sizeof(y = d + e)); //这里y只是被赋值了,并没有整型提升,所以输出的还是1
return 0;
}
//任何一个表达式都有两个属性1.值属性 2.类型属性
//int a = 3, b = 5;
//short c = 5;
//c = a + b; - 对于a+b来说,结果为8,这是值,值需要有一个类型,如果不装到c里面,则是int,如果装到c里,则类型由c决定,为short
空调维修
- 温岭冰箱全国统一服务热线-全国统一人工【7X2
- 荆州速热热水器维修(荆州热水器维修)
- 昆山热水器故障码5ER-昆山热水器故障码26
- 温岭洗衣机24小时服务电话—(7X24小时)登记报
- 统帅热水器售后维修服务电话—— (7X24小时)登
- 阳江中央空调统一电话热线-阳江空调官方售后电
- 乌鲁木齐阳春燃气灶厂家服务热线
- 珠海许昌集成灶售后服务电话-全国统一人工【
- 乌鲁木齐中央空调维修服务专线-乌鲁木齐中央空
- 新沂热水器故障电话码维修-新沂热水器常见故障
- 诸城壁挂炉24小时服务热线电话
- 靖江空调24小时服务电话-——售后维修中心电话
- 空调室外滴水管维修(空调室外排水管维修)
- 九江壁挂炉400全国服务电话-(7X24小时)登记报修
- 热水器故障码f.22怎么解决-热水器f0故障解决方法
- 营口热水器售后维修服务电话—— 全国统一人工