C语言的本质(7)——C语言运算符大全
C语言的本质(7)——C语言运算符大全
C语言的结合方向
C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左)。例如算术运算符的结合性是自左至右,即先左后右。如有表达式 x-y+z则 y 应先与“-”号结合,执行 x-y 运算,然后再执行+z 的运算。这种自左至右的结合方向就称为“左结合性”。而自右至左的结合方向称为“右结合性”。 最典型的右结合性运算符是赋值运算符。如 x=y=z,由于“=”的右结合性,应先执行 y=z 再执行 x=(y=z)运算。C语言运算符中有不少为右结合性,应注意区别,以避免理解错误。
C语言的运算符:
1、复合赋值运算符
复合赋值运算符包括*= ,/=, %=, +=, -=, <<=, >>=, &=, ^=, |=,在赋值的同时做一个运算。例如a += 1相当于a = a + 1。但有一点细微的区别,前者对表达式a只求值一次,而后者求值两次,如果a是一个复杂的表达式,求值一次和求值两次的效率是不同的,例如a[i+j] += 1和a[i+j] = a[i+j] + 1。仅仅是效率上的差别吗?对于没有Side Effect的表达式,求值一次和求值两次的结果是一样的,但对于有Side Effect的表达式则不一定,例如a[foo()] += 1和a[foo()] = a[foo()] + 1,如果foo()函数调用有SideEffect,比如会打印一条消息,那么前者只打印一次,而后者打印两次。
++i相当于i = i + 1,其实更准确地说应该是等价于i += 1,而--i等价于i -= 1。
2、条件运算符
条件运算符是C语言中唯一一个三目运算符(Ternary Operator),带三个操作数,它的形式是
表达式1 ? 表达式2 : 表达式3
这个运算符所组成的整个表达式的值等于表达式2或表达式3的值,取决于表达式1的值是否为真,可以把它想像成这样的函数:
if (表达式1)
return表达式2;
else
return表达式3;
表达式1相当于if语句的控制表达式,因此它的值必须是标量类型,而表达式2和3相当于同一个函数在不同情况下的返回值,因此它们的类型要求一致,也要做Usual Arithmetic Conversion。
下面举个例子,定义一个函数求两个参数中较大的一个。
int max(int a, int b)
{
return(a > b) ? a : b;
}
3、逗号运算符
逗号运算符也是一种双目运算符,它的形式是表达式1, 表达式2,两个表达式不要求类型一致,左边的表达式1先求值,求完了直接把值丢掉,再求右边表达式2的值作为整个表达式的值。逗号运算符是左结合的,类似于+-*/运算符,根据组合规则可以写出表达式1, 表达式2, 表达式3, ..., 表达式n这种形式,表达式1, 表达式2可以看作一个子表达式,先求表达式1的值,然后求表达式2的值作为这个子表达式的值,然后这个值再和表达式3组成一个更大的表达式,求表达式3的值作为这个更大的表达式的值,依此类推,整个计算过程就是从左到右依次求值,最后一个表达式的值成为整个表达式的值。
注意,函数调用时各实参也是用逗号隔开,那个逗号不能看作逗号运算符。但可以这样:
f(a, (t=3, t+2), c)传给函数f的有三个参数,第二个参数的值是表达式t+2的值。
4、sizeof运算符与typedef类型声明
sizeof是一个很特殊的运算符,它有两种形式:sizeof表达式和sizeof(类型名)。它的特殊之处在于,sizeof 表达式中的表达式并不求值,只是根据类型转换规则求得该表达式的类型,然后把这种类型所占的字节数作为sizeof 表达式这整个表达式的值。有些人喜欢写成sizeof(表达式)的形式也可以,这里的括号和return(1);的括号一样,没有任何作用。但另外一种形式sizeof(类型名)的括号则是必须写的,整个表达式的值也是这种类型所占的字节数。
比如用sizeof运算符求一个数组的长度:
int a[12];
printf("%d\n", sizeof a/sizeof a[0]);
在上面这个例子中,由于sizeof表达式中的表达式不需要求值,所以不需要到运行时才计算,事实上,在编译时就知道sizeof a的值是48,sizeof a[0]的值是4,所以在编译时就已经把sizeof a/sizeof a[0]替换成常量12了,这是一个常量表达式。
准确地说,sizeof表达式的值是size_t类型的,这个类型定义在stddef.h头文件中,不过你的代码中只要不出现size_t这个类型名就不用包含这个头文件,比如像上面的例子就不用包含这个头文件。size_t这个类型是我们讲过的整型中的某一种,编译器可能会用typedef做一个类型声明:
typedef unsigned long size_t;那么size_t类型就是unsignedlong类型。之所以不直接规定sizeof的值是unsigned long型的,而要规定一个size_t类型,是为了允许不同的编译器根据自己平台的情况定义size_t为不同的类型,这样使用size_t类型的代码就具有很好的可移植性,但不管编译器怎么实现,C标准明确规定sizeof的值是无符号整型的。
typedef这个关键字用于给一个类型起个新的名字,上面的声明可以这么看:去掉typedef就成了一个变量声明unsigned long size_t;,size_t是一个变量名,类型是unsigned long,那么加上typedef之后,size_t就是一个类型名,就代表unsigned long类型。再举个例子:
typedef char array_t[10];
array_t a;就相当于定义char a[10];类型名也遵循标识符的命名规则,并且通常加个_t后缀,表示Type。
C语言运算符的优先级
下面按优先级从高到低的顺序总结一下各种运算符,每一条所列的各运算符具有相同的优先级,对于同一优先级的多个运算符按什么顺序计算也有说明,双目运算符就简单地用“左结合”或“右结合”来说明了。
1、标识符、常量、字符串和用()括号套起来的表达式是组成表达式的最基本单元,在运算中做操作数,优先级最高。
2、后缀运算符,包括数组取下标[]、函数调用()、结构体取成员.、指向结构体的指针取成员->、后缀自增++、后缀自减--。如果一个操作数后面有多个后缀,按照离操作数从近到远的顺序(也就是从左到右)依次运算,比如a.name++,先算a.name,再++,这里的.name应该看成a的一个后缀,而不是把.看成双目运算符。
3、单目运算符,包括前缀自增++、前缀自减--、sizeof、类型转换()、取地址运算&、指针间接寻址*、正号+、负号-、按位取反~、逻辑非! 。如果一个操作数前面有多个前缀,按照离操作数从近到远的顺序(也就是从右到左)依次运算,比如!~a,先算~a,再求!。
4、乘*、除/、模%运算符。这三个运算符是右结合的。
5、加+、减-运算符。右结合。
6、移位运算符<<和>>。右结合。
7、关系运算符< > <=>=。右结合。
8、相等性运算符==和!=。右结合。
9、按位与&。右结合。
10、按位异或^。右结合。
11、按位或|。右结合。
12、逻辑与&&。右结合。
13、逻辑或||。右结合。
14、条件运算符:?。在第 2 节 “if/else语句”讲过Dangling-else问题,条件运算符也有类似的问题。例如a ? b : c ? d : e是看成(a ? b : c) ? d : e还是a ? b : (c ? d : e)?C语言规定是后者。
15、赋值=和各种复合赋值(*= /=%= += -= <<= >>= &= ^= |=)。左结合。
16、逗号运算符。右结合。
下面的表格详细说明了运算符的结合方向和优先级:
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
|
() |
圆括号 |
(表达式)/函数名(形参表) |
|||
. |
成员选择(对象) |
对象.成员名 |
|||
-> |
成员选择(指针) |
对象指针->成员名 |
|||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
(类型) |
强制类型转换 |
(数据类型)表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
单目运算符 |
||
-- |
自减运算符 |
--变量名/变量名-- |
单目运算符 |
||
* |
取值运算符 |
*指针变量 |
单目运算符 |
||
& |
取地址运算符 |
&变量名 |
单目运算符 |
||
! |
逻辑非运算符 |
!表达式 |
单目运算符 |
||
~ |
按位取反运算符 |
~表达式 |
单目运算符 |
||
sizeof |
长度运算符 |
sizeof(表达式) |
|||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
双目运算符 |
||
% |
余数(取模) |
整型表达式/整型表达式 |
双目运算符 |
||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
双目运算符 |
||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
双目运算符 |
||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
双目运算符 |
||
< |
小于 |
表达式<表达式 |
双目运算符 |
||
<= |
小于等于 |
表达式<=表达式 |
双目运算符 |
||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
双目运算符 |
||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
|
/= |
除后赋值 |
变量/=表达式 |
|||
*= |
乘后赋值 |
变量*=表达式 |
|||
%= |
取模后赋值 |
变量%=表达式 |
|||
+= |
加后赋值 |
变量+=表达式 |
|||
-= |
减后赋值 |
变量-=表达式 |
|||
<<= |
左移后赋值 |
变量<<=表达式 |
|||
>>= |
右移后赋值 |
变量>>=表达式 |
|||
&= |
按位与后赋值 |
变量&=表达式 |
|||
^= |
按位异或后赋值 |
变量^=表达式 |
|||
|= |
按位或后赋值 |
变量|=表达式 |
|||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
从左向右顺序运算 |
C语言的本质(7)——C语言运算符大全的更多相关文章
- C语言的本质(28)——C语言与汇编之用汇编写一个Helloword
为了更加深入理解C语言的本质,我们需要学习一些汇编相关的知识.作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但是非常重要.因为它能够完成许多其它语言所无法完成的功能.就拿 Linux 内核 ...
- C语言的本质(15)——C语言的函数接口入门
C语言的本质(15)--C语言的函数接口 函数的调用者和其实现者之间存在一个协议,在调用函数之前,调用者要为实现者提供某些条件,在函数返回时,实现者完成调用者需要的功能. 函数接口通过函数名,参数和返 ...
- C语言的本质(6)——位运算
位运算是指按二进制进行的运算.在系统软件中,常常需要处理二进制位的问题.C语言提供了6个位操作运算符.这些运算符只能用于整型操作数,即只能用于带符号或无符号的char,short,int与long类型 ...
- C语言的本质(4)——浮点数的本质与运算
C语言的本质(4)--浮点数的本质与运算 C语言规定了3种浮点数,float型.double型和long double型,其中float型占4个字节,double型占8个字节,longdouble型长 ...
- C语言的本质(3)——整数的本质与运算
C语言的本质(3)--整数的本质与运算 计算机存储的最小单位是字节(Byte),一个字节通常是8个bit.C语言规定char型占一个字节的存储空间.如果这8个bit按无符号整数来解释,则取值范围是0~ ...
- 新手小白入门C语言第六章:C运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号.C 语言内置了丰富的运算符,并提供了以下类型的运算符: 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 杂项运算符 小编将会为大家逐一介 ...
- C语言的本质(10)——指针本质
指针,大概是C语言中最难理解的概念之一了.指针这个东西是C语言中的一个基本概念,C99中对于指针的定义是: 1. 指针的类型是derived from其它类型,也就是说指针的类型是由它指向的类型决定的 ...
- C语言入门---第九章 C语言指针
没学指针就是没学C语言! 指针是C语言的精华,也是C语言的难点. 所谓指针,也就是内存的地址,所谓指针变量,也就是保存了内存地址的变量.不过人们往往不会区分两者的概念,而是混淆在一起使用. ===== ...
- 12天学好C语言——记录我的C语言学习之路(Day 12)
12天学好C语言--记录我的C语言学习之路 Day 12: 进入最后一天的学习,用这样一个程序来综合考量指针和字符串的关系,写完这个程序,你对字符串和指针的理解应该就不错了. //输入一个字符串,内有 ...
随机推荐
- 【STM32】用DMA实现多路ADC通道数据采集
今天尝试了下STM32的ADC采样,并利用DMA实现采样数据的直接搬运存储,这样就不用CPU去参与操作了. 找了不少例子参考,ADC和DMA的设置了解了个大概,并直接利用开发板来做一些实验来验证相关的 ...
- Nand flash 的发展和eMMC
讨论到eMMC的发展历程,必须要从介绍Flash的历史开始 Flash分为两种规格:NOR Flash和NAND Flash,两者均为非易失性闪存模块. 1988年,Intel首次发出NOR flas ...
- QT5中如何使用QFtp类(这个类虽然没有被收录,但一直在更新)
由于QT5对QML的支持有很大的改进,所以打算将原来基于QT4的程序移植到QT5上,在移植用QFtp类写的程序时傻眼了! Qt5 移除了 QFtp API,原因是其实现质量.QNetworkAcces ...
- Android开发之ExpandableListView扩展(BaseExpandableListAdapter的使用)(完整版)
Android开发之ExpandableListView扩展(BaseExpandableListAdapter的使用)(完整版)
- 【IPC通信】基于管道的popen和pclose函数
http://my.oschina.net/renhc/blog/35116 [IPC通信]基于管道的popen和pclose函数 恋恋美食 恋恋美食 发布时间: 2011/11/12 23:20 ...
- 【C/C++】Linux下system()函数引发的错误
http://my.oschina.net/renhc/blog/54582 [C/C++]Linux下system()函数引发的错误 恋恋美食 恋恋美食 发布时间: 2012/04/21 11:3 ...
- checkbook全选/反选/全不选
<!doctype html><html lang="en"><head> <meta charset="UTF-8" ...
- Newton‘ method 的优缺点
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzE1Mjg5NQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...
- 关于Oracle数据库字符集的选择
如果数据库只在中国地区使用,数据库字符集选择ZHS16GBK或者常用中文字符集,如果不确定,就推荐使用AL32UTF8 国家字符集就选择: AL16UTF16 字符集一旦设定,不允许修改,修改可能出现 ...
- 【斐波拉契+数论+同余】【ZOJ3707】Calculate Prime S
题目大意: S[n] 表示 集合{1,2,3,4,5.......n} 不存在连续元素的子集个数 Prime S 表示S[n]与之前的所有S[i]互质; 问 找到大于第K个PrimeS 能整除X 的第 ...