概述
在C语言中,表达式和语句是构成程序的基本元素。本节和下一章节我们就围绕它们展开讲一讲其中的C语言基础语法。
首先,让我们区分这两个概念:
-
语句(statement),语句是代码中的一个完整的,可以执行的步骤。
- 语句要么以";"分号结尾(简单语句),要么以"{}"代码块结尾(复合语句)
- 语句的作用复杂多样,常用于构建程序逻辑,如循环语句、条件判断语句、跳转语句等。
-
表达式(expression),表达式是由变量、常量(称之为操作数)和运算符(也叫操作符)组成的序列,它总是会计算出一个值。
- 表达式可以非常简单,如一个单独的常量或变量,或者非常复杂,如包含多个运算符和函数调用的组合。
- 表达式的作用就是计算值、赋值、函数调用等。
在C语言中,语句和表达式实际上并没有明显的绝对界限,它们的关系是:
- 任何表达式只要直接加上一个分号,立刻就会成为一条语句。比如
a = 10是一个赋值表达式,但只要加上";",就会变成一个赋值语句。表达式语句是语句最简单的形式。 - 语句不仅限于表达式,比如选择、循环等语句,语句可以更多的影响程序的逻辑。
在表达式中,最重要、最核心的就是连接表达式中常量、变量的运算符了,所以本小节我们主要研究C语言的运算符。
C 语言拥有异常丰富的运算符,比较常见和常用的有以下运算符:
- 算术运算符
- 赋值运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 三目运算符
这些运算符,又可以根据操作数的多少,分为:
- 一元运算符,只需要一个操作数的运算符
- 二元运算符,需要两个操作数的运算符,多数运算符都属于二元运算符。
- 三目运算符,自然就是需要三个操作数的运算符。
以上都比较简单,只介绍位运算符。
位运算符
移位运算符
它们分别是左移位运算符(<<)和右移位运算符(>>)。
移位运算符,可以通过将整数数据的二进制位向左或向右移动,来变换整数的二进制表示,从而改变整数值。
下面用两个表达式来描述移位运算符的基本作用,其中i是整数,j是一个正整数:
-
i << j:将i的位模式向左移动j位。- 移出去的高位丢弃,并在低位补0。
- 实际上该操作相当于将
i乘以 2j(如果没有溢出的话) - 此表达式的主要作用是返回
i移位运算后的结果 - 此表达式一般没有副作用,不会改变
i变量的取值,移位运算符没有赋值的作用!!
-
i >> j:将i的位模式向右移j位。- 移出去的低位丢弃,如果
i是无符号数或者非负值,则在左边补 0。 - 如果 i 是负值,其结果会根据编译器平台实现不同而不同:一些实现会在左边补 0,一些实现会在左边补 1。
- 在
i是非负数的情况下,实际上该操作相当于将i除以2j(注意整数除法的结果还是整数) - 此表达式的主要作用是返回
i移位运算后的结果 - 此表达式一般没有副作用,不会改变
i变量的取值,移位运算符没有赋值的作用!!
- 移出去的低位丢弃,如果
注意:左移时如果发生了数据溢出将产生未定义行为,所以程序员在执行左移位运算时,需要认真思考移位的极限,避免溢出产生
编程语言中的右移位运算符(不仅仅C语言),通常有两种行为表现:
-
算术右移:算术右移会在高位插入符号位的副本(对于负数是1,正数是0),算术右移位运算通常用于有符号数,因为它可以保持整数的符号不变。
- 对一个负数进行算术右移会在高位补1。
- 对一个正数进行算术右移会在高位补0。
- 逻辑右移:逻辑右移不考虑整数的符号,总是在高位补0。逻辑右移显然不适用于移位有符号数,它用于移位无符号数。
C语言标准中规定,在右移位无符号数时,采用逻辑右移。但当右移位有符号数时,采取上述哪种行为取决于编译器平台的实现。
当然,在大多数现代编译器和平台上(比如MSVC/GCC/Clang等),右移位运算符都是算术右移的,也就是在现代编译平台上,右移位运算符不会改变有符号数的符号。
综上所述,关于右移位运算符的使用我们提出以下建议:
- 为了更好的平台移植性和安全性,可以直接对无符号数做右移位运算,这样就不会出现符号问题了。
- 如果要对有符号数做移位运算,请自行确认编译器行为,避免跨平台时出现移位符号问题。
扩展:
由于C语言在右移位运算上的灵活性,可能会导致一些"编码陷阱",所以C以后的高级编程语言一般都会对右移位运算符做更多的限制和规定。比如:
- Java会直接提供两个右移位运算符:算术右移(
>>)和逻辑右移(>>>) - Python直接禁用了逻辑右移,总是执行算术右移。
按位运算符
按位位运算符包含:按位取反~,按位与&,按位异或^,按位或|。其中按位取反是一元运算符,其余都是二元运算符。
对于以下表达式:
~a:对a进行按位取反运算。也就是将 a 的每一位进行取反操作。即 0 变成 1,1 变成 0。
i & j:对 i 和 j 的每一位进行逻辑与运算。只有两个数的相应位都为1时,结果位才为1,否则为0。
i | j:对 i 和 j 的每一位进行逻辑或运算。只要两个数的相应位中有一个为1,结果位就为1,否则为0。
i ^ j:对 i 和 j 的每一位进行异或运算。如果两个数的相应位一个为0,另一个为1,则结果位为1,否则为0。(对应位不同结果就是1)
注意:
- 这些表达式的主要作为是返回位运算后得到的结果,没有副作用,不会改变任何一个操作数的值!!
- 在C语言中,
&、|不是逻辑运算符,而是位运算符,不要搞混淆了。
按位运算符中,比较需要留意的是按位异或。它具有以下一些非常优秀的性质:
a ^ 0 = a; 任何整数异或0得到的都是它本身
a ^ a = 0; 任意整数异或自己得到的都是0
a ^ b = b ^ a; 异或运算满足交换律
(a ^ b) ^ c = a ^ (b ^ c); 异或运算满足结合律
1852

被折叠的 条评论
为什么被折叠?



