相信你在大学的时候
遇到过类型转换题
不知道有多少人对它会感到头疼
可能有些人觉得记不住
有些人理解不了
因此我来介绍一下我的记忆方法以及对此应有的理解
I.两种类型转换
类型转换分两种:
- 隐式类型转换(自动转换):编译器在编译时自动进行转换。
- 显式类型转换(强制转换):在代码编写时明确指定进行转换,格式为:
(目标类型) 表达式
。比方说(int)char = ‘A’就是在强制把char的类型转换成int。
而在做类型转换题,主要考的是隐式转换
II.隐式类型转换的四大规则
整体而言
这些规则讲究个:
向数据范围更大的类型转换,以保证精度不丢失。
这并不多难
因为在你学数据类型时
你就应该能记住char short int......这些以占用空间来计算的从小到大排列的顺序
当你能记住它们时
记这个也就很简单了
规则一:整型提升
在表达式计算中
所有比 int
类型“小”的整型(如 char
, short
等)会首先被自动转换为 int
或 unsigned int
然后再参与计算
这是因为CPU处理 int
类型效率最高
char a = 10, b = 20;
int c = a + b; // a和b先被提升为int,然后相加,结果也是int
规则二:赋值转换
当你进行赋值操作时,如a = b(a,b都是变量时),等号右边表达式的结果类型会转换为左边变量的类型
不要问为什么,因为这是C语言人为规定的语法
但是注意: 这可能发生“截断”或“精度丢失”:
- 浮点数 -> 整数:直接丢弃小数部分。
- 长整型 -> 短整型:截掉高位字节,只保留低位字节。
例如这行代码:
int i;
float f = 3.14;
i = f; // i的值是3,小数部分被丢弃
long l = 0x12345678;
short s = l; // s的值是0x5678 (取决于系统字节顺序)
规则三:运算转换
当一个操作符(如 +
, -
, *
, /
)的两个操作数类型不同时
编译器会将它们转换为共同的类型
而它遵循的顺序就是我前面说的当你记住char short int......时你比较好记的了
转换方向遵循以下层次(从下到上):
long double
(最高)
↑
double
↑
float
↑
unsigned long long
↑
long long
↑
unsigned long
↑
long
↑
unsigned int
↑
int
(最低)
有没有发现什么规律呢?
我来帮你总结我发现的总方向
你看看是不是这样:
由整数到小数,由原本到unsigned,由原本到更long,由小(粗略)到大(精准)
当你也发现这个规律的时候,你就会发现它们很好记了
int i = 10;
float f = 2.5;
double d = i + f;
// 步骤1: i (int) 和 f (float) 类型不同
// 步骤2: 根据规则,int 向 float 转换?不对!看层次图,float在上,int在下。
// 正确顺序是:i 先被转换为 float,然后与 f 进行加法,得到 float 类型的结果
// 步骤3: 赋值给 d,float 结果再转换为 double
规则四:函数参数转换
在调用函数时
如果形参和实参类型不匹配
实参会转换为形参的类型
(在ANSI C之后,通常需要类型匹配,但老标准或某些情况仍会发生转换)
III.记忆程度
如果你做过类型转换的题目时
你可能会发现
很多类型都有它的范围
超出范围的值会溢出
那我们需不需要记住它们的确切范围呢?
答案是:不需要死记硬背所有细节,但必须掌握核心概念和关键范围。
你不需要像背课文一样记住 unsigned long long
的最大值
18,446,744,073,709,551,615
但你必须有这些大致的概念:
char / unsigned char | 非常小。char 大约 ±128,unsigned char 大约 0~255。 | |
short / unsigned short | 比较小。大约 ±3万2千, 无符号大约 6万5千。 | |
int | 足够大。大约 ±21亿。 | |
unsigned int | 很大。大约 0~42亿。 | |
float | 有小数,但精度有限。 | |
double | 精度更高。 |
其中你要记的数字就是这么些
这样足以应付绝大多数题目和实际工程
例如:
unsigned char a = 200;
unsigned char b = 100;
unsigned char c = a + b; // 请问c的值是多少?
计算 a + b
。a
和 b
都是小类型,会发生整型提升
被提升为 int
(通常是更大的类型)
200 + 100 = 300
,这个结果是一个 int
类型的 300
现在要将 int
类型的 300
赋值给 unsigned char
类型的 c
由于我知道 unsigned char
的范围很小(最大255)
而300 > 255,所以一定会发生溢出/截断
300
的二进制形式中,低8位的值就是最终存入 c
的值
300 - 256 = 44
。所以 c = 44
在整个推理过程中
我只需要知道 unsigned char
的最大值是255左右
而不需要知道它精确是255
我知道“300 > 最大值”这个事实就足够我判断出会发生溢出了
而实际工程中,我们就干脆可以用代码来查询了
比方说:
#include <stdio.h>
#include <limits.h> // 整数极限
#include <float.h> // 浮点数极限
int main() {
printf("The maximum value of char is: %d\n", CHAR_MAX);
printf("The maximum value of int is: %d\n", INT_MAX);
printf("The maximum value of unsigned int is: %u\n", UINT_MAX);
printf("The maximum value of float is: %e\n", FLT_MAX);
return 0;
}
这就更不必非常严苛去记忆它们的范围了
IV.类型转换的意义
char a = 10, b = 20;
int c = a + b; // a和b先被提升为int,然后相加,结果也是int
不知道你在看到这段代码的时候
你有没有疑惑过
为什么我不直接用int来定义a,b呢?
为什么我偏要用char来定义
然后让它们自己转换呢?
这是因为char
通常占用 1字节
int
在大多数现代系统上占用 4字节
这也就是说
当你用int来定义a,b时
你要用更多的空间来储存a,b
可能用char来定义会多上转换的一步
使得代码计算效率稍微逊色一点
但它的空间占有确实只有int的四分之一
当数据量巨大时,节省的空间非常可观!
也就是说
这是空间效率与计算效率的取舍
而这也就是类型转换的意义所在