C语言表达式与类型转换详解
立即解锁
发布时间: 2025-08-22 00:24:53 阅读量: 8 订阅数: 16 


C语言编程基础与实践
# C语言表达式与类型转换详解
## 1. 操作数类型转换规则
在进行运算时,操作数的类型转换遵循以下规则:
- 如果一个操作数是 `long int`,另一个是 `unsigned int`,则效果取决于 `long int` 是否能表示 `unsigned int` 的所有值。若可以,`unsigned int` 操作数转换为 `long int`;若不可以,两者都转换为 `unsigned long int`。
- 若一个操作数是 `long int`,另一个转换为 `long int`。
- 若任一操作数是 `unsigned int`,另一个转换为 `unsigned int`。
- 否则,两个操作数类型均为 `int`。
此外,有两个重要变化:一是对 `float` 操作数的算术运算可以采用单精度,而非之前规定的双精度;二是较短的无符号类型与较大的有符号类型结合时,无符号属性不会传播到结果类型,避免了一些意外情况,但在无符号表达式与相同大小的有符号表达式比较时仍可能出现意外结果。
## 2. 指针与整数的操作
### 2.1 指针与整数的加减
- 整数类型的表达式可以与指针进行加减操作,整数表达式会按照加法运算符的规则进行转换。
- 同一数组中相同类型对象的两个指针可以相减,结果会按照减法运算符的规则转换为整数。
### 2.2 指针与整数的转换
- 值为 0 的整数常量表达式,或转换为 `void *` 类型的此类表达式,可以通过强制类型转换、赋值或比较转换为任何类型的指针,得到的是空指针。
- 指针可以转换为足够大的整数类型,所需大小和映射函数依赖于具体实现。
- 一种类型的指针可以转换为另一种类型的指针,但可能会导致寻址异常,不过指针转换为 `void *` 类型再转换回来不会改变。
- 指针可以转换为除了所指对象类型的限定符有增减外其他相同的指针类型。
### 2.3 函数指针的转换
函数指针可以转换为另一种函数类型的指针,调用转换后的指针所指定的函数依赖于具体实现,但将转换后的指针再转换回原始类型,结果与原始指针相同。
以下是指针与整数操作的规则总结表格:
| 操作类型 | 规则描述 |
| ---- | ---- |
| 指针与整数加减 | 整数表达式按加法运算符规则转换 |
| 指针相减 | 结果按减法运算符规则转换为整数 |
| 整数常量转指针 | 值为 0 的整数常量表达式可转任何类型指针 |
| 指针转整数 | 转换大小和映射函数依赖实现 |
| 指针类型转换 | 可能导致寻址异常,`void *` 转换无变化 |
| 函数指针转换 | 调用依赖实现,转回原始类型结果相同 |
## 3. `void` 类型相关
### 3.1 `void` 对象的值使用
`void` 对象不存在值,不能以任何方式使用,也不能进行显式或隐式转换为非 `void` 类型。`void` 表达式只能在不需要值的地方使用,如作为表达式语句或逗号运算符的左操作数。
### 3.2 表达式转换为 `void` 类型
表达式可以通过强制类型转换转换为 `void` 类型,例如使用 `void` 强制类型转换来表明丢弃函数调用的值。
### 3.3 `void *` 指针
任何对象的指针都可以无损地转换为 `void *` 类型,再转换回原始指针类型时能恢复原始指针。与一般的指针到指针转换不同,`void *` 指针的赋值和比较不需要显式的强制类型转换。
下面是 `void` 类型相关操作的流程图:
```mermaid
graph TD;
A[表达式] -->|强制类型转换| B[void 类型表达式];
C[对象指针] -->|转换| D[void * 指针];
D -->|转换| C;
```
## 4. 表达式相关规则
### 4.1 表达式运算符优先级和结合性
表达式运算符的优先级与各主要子部分的顺序相同,优先级高的先计算。在每个子部分内,运算符具有相同的优先级,左结合性或右结合性在各子部分中分别指定。语法规则包含了运算符的优先级和结合性。
### 4.2 表达式的求值顺序
运算符的优先级和结合性是明确的,但表达式的求值顺序除某些特殊情况外是未定义的。除非运算符的定义保证其操作数按特定顺序求值,否则实现可以自由选择操作数的求值顺序,甚至可以交错求值。
### 4.3 表达式异常处理
表达式求值中的溢出、除零检查和其他异常的处理未在语言中定义。大多数 C 语言实现忽略有符号整数表达式和赋值中的溢出,但这并非保证行为。不同实现对除零和浮点异常的处理方式不同,有时可以通过非标准库函数进行调整。
## 5. 表达式类型转换与分类
### 5.1 指针转换
如果表达式或子表达式的类型是 “`T` 数组”,则表达式的值是指向数组第一个对象的指针,类型变为 “指向 `T` 的指针”。但在一元 `&` 运算符、`++`、`--`、`sizeof` 运算符的操作数中,或作为赋值运算符或 `.` 运算符的左操作数时,此转换不发生。类似地,“返回 `T` 的函数” 类型的表达式,除作为 `&` 运算符的操作数外,会转换为 “指向返回 `T` 的函数的指针”。
### 5.2 基本表达式
基本表达式包括标识符、常量、字符串或括号内的表达式:
```plaintext
primary-expression
identifier
constant
string
(expression)
```
- 标识符:若已正确声明,其类型由声明指定。若引用对象且类型为算术、结构、联合或指针类型,则为左值。
- 常量:类型取决于其形式。
- 字符串字面量:最初类型为 “`char` 数组”,通常会转换为 “指向 `char` 的指针”。
- 括号内的表达式:类型和值与未加括号的表达式相同,括号的优先级不影响表达式是否为左值。
### 5.3 后缀表达式
后缀表达式的运算符从左到右结合,包括以下几种形式:
```plaintext
postfix-expression:
primary-expression
postfix-expression[expression]
postfix-expression(argument-expression-listopt)
postfix-expression.identifier
postfix-expression->identifier
postfix-expression++
postfix-expression--
```
#### 5.3.1 数组引用
后缀表达式后跟方括号内的表达式表示数组引用,一个表达式必须是 “指向 `T` 的指针” 类型,另一个必须是整数类型,结果类型为 `T`,`E1[E2]` 等同于 `*((E1)+(E2))`。
#### 5.3.2 函数调用
函数调用是后缀表达式(函数指示符)后跟括号,括号内是逗号分隔的赋值表达式列表作为函数参数。若后缀表达式是未声明的标识符,会隐式声明为 `extern int identifier();`。函数调用的值类型为函数返回类型。函数参数传递是按值传递,函数可以修改参数对象的值,但不影响实际参数的值。函数声明有新老两种风格,新风格明确指定参数类型,老风格不指定。不同风格下参数的处理规则不同,且参数求值顺序未指定。
#### 5.3.3 结构引用
后缀表达式后跟点和标识符,或后跟箭头和标识符表示结构引用。前者第一个操作数必须是结构或联合,后者第一个操作数必须是指向结构或联合的指针,结果是结构或联合的指定成员。若类型不是数组类型,结果为左值。
#### 5.3.4 后缀自增自减
后缀表达式后跟 `++` 或 `--` 运算符,表达式的值是操作数的原始值,操作数随后自增或自减 1。操作数必须是左值,结果不是左值。
### 5.4 一元运算符
一元运算符的表达式从右到左结合,形式如下:
```plaintext
unary-expression:
postfix expression
++unary expression
--unary expression
unary-operator cast-ex
```
0
0
复制全文
相关推荐









