前言:
几乎每一个程序都需要进行运算,对数据进行加工处理,否则程序就没有意义了。要进行运算,就需规定可以使用的运算符。C语言的运算符范围很宽,把除了控制语句和输入输出以外几乎所有的基本操作都作为运算符处理,例如将赋值符“=”作为赋值运算符、方括号作为下标运算符等。
3.3.1 C运算符
### 1. 算术运算符
- **+** :加法
- **-** :减法
- **\*** :乘法
- **/** :除法
- **%** :求余数
### 2. 关系运算符
- **==** :等于
- **!=** :不等于
- **<** :小于
- **>** :大于
- **<=** :小于或等于
- **>=** :大于或等于
### 3. 逻辑运算符
- **&&** :逻辑与
- **||** :逻辑或
- **!** :逻辑非
### 4. 赋值运算符
- **=** :赋值
- **+=** :加法赋值 (例如 x += 5 是 x = x + 5 的缩写)
- **-=** :减法赋值
- **\*=** :乘法赋值
- **/=** :除法赋值
- **%=** :求余赋值
### 5. 位运算符(这个知识点在Leetcode461.汉明距离中出现)
传送门:汉明距离
- **&** :按位与
- **|** :按位或
- **^** :按位异或
- **~** :按位非
- **<<** :左移
- **>>** :右移
具体介绍:
位运算符在C语言中用于对整数在位级别上进行操作。这些运算符主要针对整数的二进制表示来工作,因此理解整数的二进制表示方式对于理解位运算是很有帮助的。
### 1. 位运算符概览
- **&** :按位与
- **|** :按位或
- **^** :按位异或
- **~** :按位非
- **<<** :左移
- **>>** :右移
### 2. 详细讲解与应用
#### 2.1 **&** (按位与)
**用途**:只有当两个对应的位都为1时,结果才为1。
**实例**:
```c
int a = 5; // 0101 (二进制)
int b = 3; // 0011 (二进制)
int result = a & b; // 结果为 1 ,即 0001 (二进制)
```
#### 2.2 **|** (按位或)
**用途**:只要两个对应的位之一为1,结果就为1。
**实例**:
```c
int result = a | b; // 结果为 7,即 0111 (二进制)
```
#### 2.3 **^** (按位异或)
**用途**:当两个对应的位不相同时,结果为1。
**实例**:
```c
int result = a ^ b; // 结果为 6,即 0110 (二进制)
```
#### 2.4 **~** (按位非)
**用途**:反转所有位。
**实例**:
```c
int result = ~a; // 结果是 -6。原因是现代计算机使用补码表示负数,所以这是补码表示的 -6。
```
#### 2.5 **<<** (左移)
**用途**:将数值的所有位向左移动指定的位数。
**实例**:
```c
int result = a << 1; // 结果为 10。因为 5 (即 0101) 向左移动一位变为 1010,即十进制的10。
```
#### 2.6 **>>** (右移)
**用途**:将数值的所有位向右移动指定的位数。
**实例**:
```c
int result = a >> 1; // 结果为 2。因为 5 (即 0101) 向右移动一位变为 0010,即十进制的2。
```
### 3. 位运算的实践应用:
1. **快速判断奇偶**:`if (x & 1) { /* 奇数 */ } else { /* 偶数 */ }`
2. **交换两个数**:`a = a ^ b; b = a ^ b; a = a ^ b;` (不需要额外的存储空间)
3. **检查特定位是否设置**:`if (x & (1 << n)) { /* 第n位被设置了 */ }`
4. **设置特定位**:`x |= (1 << n);`
5. **清除特定位**:`x &= ~(1 << n);`
6. **翻转特定位**:`x ^= (1 << n);`
了解位运算的内部机制和它们的实际应用可以帮助开发者编写更高效的代码,特别是在资源有限或性能要求较高的环境中。
### 6. 其他运算符
- **sizeof()** :返回数据类型或变量的大小(以字节为单位)。
- **& (取地址运算符)** :获取变量的地址。
- **\* (指针运算符)** :指针变量所指向的值。
- **? : (三目运算符)** :条件运算符,也称为三元运算符。
每类运算符都有其特定的使用场景和优先级。理解它们的作用和如何正确地在代码中使用它们是掌握C语言的关键。
3.3.2 基本的算术运算符
3.3.3 自增自减运算符
我的理解:
### 3.3.3 自增(++)、自减(--)运算符
自增 (`++`) 和自减 (`--`) 运算符在编程中非常常见,它们用于简化对变量加1或减1的操作。
- **前缀模式**(`++i` 或 `--i`):在其他操作发生前,变量值先进行加1或减1。
- **后缀模式**(`i++` 或 `i--`):其他操作发生后,变量值才进行加1或减1。
例如:
1. `j=++i;`:先增加i的值,然后将i的新值赋给j。
2. `j=i++;`:先将i的当前值赋给j,然后再增加i的值。
但请注意,过多地在复杂的表达式中使用这些运算符会降低代码的可读性。为了编写清晰、易于维护的代码,最好仅在简单的情境下使用自增和自减运算符,并避免在同一语句中混合多种操作。
3.3.4 算术表达式和运算符的优先性与结合性
我的理解:
### 3.3.4 算术表达式和运算符的优先级与结合性
在C语言中,表达式由操作数和运算符组成,而每个运算符都有相应的优先级和结合性。
- **优先级**:定义了在没有明确的括号时,哪些运算符首先被求值。例如,乘法和除法的优先级高于加法和减法,所以在 `a-b*c` 中,先执行乘法操作。
- **结合性**:当两个运算符优先级相同时,结合性决定了哪个运算符首先被评估。大多数算术运算符从左到右进行评估,即它们具有左结合性。
使用正确的优先级和结合性规则是很重要的,因为它们可以影响到表达式的结果。为了避免混淆和潜在的错误,最佳的做法是在可能存在歧义的地方使用括号来明确你的意图。
这两个部分的核心是理解自增和自减运算符如何工作,以及如何根据运算符的优先级和结合性来正确地评估复杂的表达式。
好的,我会为你解释这段话并强调其核心概念。
首先,当我们讨论表达式的**结合性**时,我们是在讨论当有多个相同优先级的运算符出现在表达式中时,该如何执行它们。
### **左结合性 (自左至右)**
大部分的运算符在C语言中是具有左结合性的。这意味着当你有一个像 `a - b + c` 这样的表达式时,由于减法和加法有相同的优先级,根据左结合性原则,你首先计算 `a - b`,然后再加上 `c`。所以表达式实际上是 `(a - b) + c`。
### **右结合性 (自右至左)**
赋值运算符和某些其他的运算符在C中是具有右结合性的。考虑一个表达式 `a = b = c`,根据右结合性原则,你首先为 `b` 赋值 `c`,然后再为 `a` 赋值 `b` 的结果(也就是 `c` 的值)。因此,这个表达式实际上被解释为 `a = (b = c)`。
### **结合性在C语言中的特性**
虽然大多数编程语言都定义了运算符的优先级,但并非所有的编程语言都明确地定义了运算符的结合性。在C语言中,结合性是明确定义的,这是它与某些其他语言的一个区别。
### **实践建议**
对于日常编程来说,记住算术运算符具有左结合性,而赋值运算符具有右结合性是很有用的。但是,为了避免误解和编程错误,最佳实践是在不确定或可能引起混淆的情况下使用括号来明确你的意图。
这样做不仅可以确保正确性,还可以提高代码的可读性,使其他开发者更容易理解你的代码。
3.3.5 不同数据间的混合运算
我的理解:
这段内容解释了在C语言中进行不同数据类型的混合运算时如何进行自动类型转换,并通过一个例子详细说明了这一点。
### **混合运算及其规律**
1. **不同数据类型的混合运算**: 当涉及到不同类型的数据进行运算时(如 `int` 和 `double`),C语言会自动进行类型转换,使二者成为同一种类型后再进行运算。
2. **转换规律**:
- 当运算涉及 `float` 或 `double` 时,结果是 `double` 类型。
- 当 `int` 与 `float` 或 `double` 进行运算时,所有参与的数据都将转换为 `double` 类型,并返回 `double` 类型的结果。
- 当字符型 (`char`) 数据与整数型进行运算时,实际上是字符的ASCII值与整数进行的运算。例如, `12 + 'A'` 是 `12 + 65 = 77`。如果字符型数据与实型数据进行运算,则将字符的ASCII值转换为 `double` 类型后再进行运算。
### **混合运算的例子**
对于给定的表达式 `10+'a'+i*f-d/3`:
- `'a'` 转为 ASCII 值,为 97。
- 因为乘法优先级高于加法,先进行 `i*f` 运算。i和f都转为 `double` 类型后进行运算,结果为 `7.5`。
- 整数107 (`10 + 97`) 与 `i*f` 的结果 `7.5` 相加,得到 `114.5`。
- `d/3` 的结果为 `2.5`。
- `114.5 - 2.5` 的结果为 `112.0`。
### **例子【例3.3】**
这个例子解决了如何将大写字母转换为小写字母。字符在内存中以ASCII码形式存储,因此可以通过算术运算进行字符转换。
要转换大小写字母,需要了解大写和小写之间的关系:小写字母的ASCII值比对应的大写字母的值大32。例如,`'a'` 的 ASCII 值是 97,而 `'A'` 的值是 65。
利用这一特性,可以通过为大写字母加32得到对应的小写字母。
这些信息对于理解C语言中的数据类型转换和混合运算非常重要,并强调了编程时要注意的细节。
3.3.6 强制运行转换运算符
我的理解:
好的,这段话讲述的是C语言中关于类型转换的概念,尤其是“强制类型转换”。
**强制类型转换**:在编程时,有时我们需要将一个数据类型的变量或表达式转换为另一个数据类型,这种手动进行的类型转换被称为“强制类型转换”。
**如何进行强制类型转换**:要进行强制类型转换,你需要在要转换的变量或表达式前加上`(目标数据类型)`。例如,`(double)a`将变量a转换为double类型。
**注意事项**:
1. 当你使用强制类型转换时,实际上是生成了一个新的、所需类型的中间值。原始变量的数据类型和值都不会发生变化。
2. 你必须确保在强制类型转换时使用括号正确地包围整个表达式。例如,如果你写`(int)x + y`,那么只有x会被转换为int,然后再加上y。但如果你写`(int)(x + y)`,则x和y的和会被转换为int。
**自动类型转换 vs 强制类型转换**:
1. **自动类型转换**:有时,当两个不同类型的数据参与运算时,编译器会自动进行类型转换。例如,在`3 + 6.5`中,整数3会自动转换为浮点数,然后与6.5相加,结果为浮点数。
2. **强制类型转换**:当自动类型转换不能满足我们的需求时,我们需要手动进行强制类型转换。例如,`%`运算符需要两边都是整数,如果x是浮点数,那么`x % 3`是非法的,你必须使用`(int)x % 3`。
在函数调用时,为了使实参与形参的类型一致,有时我们也会使用强制类型转换。例如,如果函数期望一个int类型的参数,但你有一个double变量,你可以通过强制类型转换将它转换为int然后传递。
总的来说,强制类型转换是C语言中一个非常有用的工具,它允许开发者明确地指定数据应该如何被处理,而不是依赖于编译器的自动类型转换。但也要注意使用它的正确方法,并了解它的限制和潜在风险。
总结:
**3.3 运算符和表达式**的重点、难点和易错点如下:
**重点**:
1. **运算符的种类与功能**:理解各种运算符(如算术、关系、逻辑、赋值、条件、位等)的功能和使用方法。
2. **运算符的优先级和结合性**:理解哪些运算符优先于其他运算符进行计算,并知道运算是从左到右还是从右到左。
3. **数据类型的自动与强制转换**:知道在哪些情况下编译器会自动转换数据类型,以及如何手动进行强制类型转换。
**难点**:
1. **复合表达式的求值**:当一个表达式包含多个运算符和操作数时,理解其求值的过程可能会有些困难,特别是当它们的优先级和结合性相互影响时。
2. **强制类型转换的应用**:知道何时和如何正确地应用强制类型转换,以避免不期望的结果。
3. **字符型与整型之间的运算**:例如,使用字符的ASCII值进行运算可能会令初学者感到困惑。
**易错点**:
1. **优先级和结合性的误解**:不正确地认为某个运算符比另一个优先,或误解其结合方向,从而得到错误的结果。
2. **不正确的类型转换**:不明确数据类型的自动转换规则,或者在需要的情况下没有应用强制类型转换。
3. **忽略整型溢出**:当整数运算的结果超出其数据类型能表示的范围时,可能会导致溢出,而这通常不会引发错误,但会导致不正确的结果。
4. **字符与整数的混淆**:例如,将数字字符(如'3')与实际的整数(如3)混淆。
5. **强制转换的格式错误**:如在进行强制类型转换时忘记使用括号。
总之,理解运算符和表达式是学习C语言的基础部分。要确保深入理解每个概念,并多做练习,以便掌握各种情况下的正确应用方法。