目录
1 自动类型转换(隐式转换)
1.1 运算过程中的自动类型转换
不同类型的数据进行混合运算,会发生数据类型转换,窄类型会自动转为宽类型,这样不会造成精度损失。
1.1.1 转换规则
- 不同类型整数进行运算,窄类型整数自动转换为宽类型整数。
- 小于 int 类型的整数会被自动提升为 int 类型,小于 long 类型的整数会被自动提升为 long 类型,以此类推。
- 当有符号整数与无符号整数进行运算时,有符号整数会被转换为无符号整数。这可能会导致意外的结果,特别是当有符号整数为负数时。
- 不同类型浮点数进行运算,精度小的浮点数类型自动转换为精度大的浮点数类型。
- 整数与浮点数进行运算,整数自动转换为浮点数。
尽管 long long 类型的存储空间较大(8 字节),而 float 类型的存储空间较小(4 字节),但在进行运算时,long long 会自动转换为 float,因为 float 能表示的数据范围更大。简单来说,整数与浮点数进行运算时,整数会自动转换为浮点数。
1.1.2 案例演示
#include <stdio.h>
int main()
{
// 整型提升
// 在表达式中,小于 int 类型的整数(如 short, char)会被提升为 int 类型进行运算
char ch1 = 10;
short s1 = 10;
int n1 = 40000;
// ch1 和 s1 在参与加法运算时会被自动提升为 int 类型
printf("ch1 + n1 = %d,sizeof(ch1 + n1) = %zu\n", ch1 + n1, sizeof(ch1 + n1)); // 输出:ch1 + n1 = 40010,sizeof(ch1 + n1) = 4
printf("s1 + ch1 = %d,sizeof(s1 + ch1) = %zu\n", s1 + ch1, sizeof(s1 + ch1)); // 输出:s1 + ch1 = 20,sizeof(s1 + ch1) = 4
// 有符号整数自动转为无符号整数
// 当有符号整数与无符号整数进行运算时,有符号整数会被转换为无符号整数
// 注意:这可能会导致意外的结果,特别是当有符号整数为负时
int n2 = -100;
unsigned int n3 = 20;
// -100 作为有符号整数(1111 1111 1111 1111 1111 1111 1001 1100)转换为无符号整数时,会变成一个非常大的数:4294967196(补码原理)
// 然后与 n3 相加,结果是一个无符号整数
printf("n2 + n3 = %u,sizeof(n2 + n3) = %zu\n", n2 + n3, sizeof(n2 + n3)); // 输出:n2 + n3 = 4294967216,sizeof(n2 + n3) = 4
// 使用 %d 来输出
printf("n2 + n3 = %d\n", n2 + n3); // n2 + n3 = -80
// 不同类型的浮点数运算,精度低的转为精度高的
// 当 float 和 double 类型进行运算时,float 会被提升为 double 类型进行运算
float f1 = 1.25f;
double d2 = 4.58667435;
// f1 在参与加法运算时会被自动提升为 double 类型,然后与 d2 相加
printf("f1 + d2 = %.10f,sizeof(f1 + d2) = %zu\n", f1 + d2, sizeof(f1 + d2)); // 输出:f1 + d2 = 5.8366743500,sizeof(f1 + d2) = 8
// 整型与浮点型运算,整型转为浮点型
// 当整型与浮点型进行运算时,整型会被提升为浮点型进行运算
int n4 = 10;
double d3 = 1.67;
// n4 在参与加法运算时会被自动提升为 double 类型,然后与 d3 相加
printf("n4 + d3 = %f,sizeof(n4 + d3) = %zu\n", n4 + d3, sizeof(n4 + d3)); // 输出:n4 + d3 = 11.670000,sizeof(n4 + d3) = 8
long long ll = 3523235;
float f2 = 1.353543;
printf("ll + f2 = %f,sizeof(ll + f2) = %zu\n", ll + f2, sizeof(ll + f2)); // 输出:ll + f2 = 3523236.250000,sizeof(ll + f2) = 4
return 0;
}
输出结果如下所示:
1.1.3 有符号整数与无符号整数的运算
对于下面这段代码,为什么 %u 与 %d 输出结果不同?
// 有符号整数自动转为无符号整数
// 当有符号整数与无符号整数进行运算时,有符号整数会被转换为无符号整数
// 注意:这可能会导致意外的结果,特别是当有符号整数为负时
int n2 = -100;
unsigned int n3 = 20;
// -100 作为有符号整数(1111 1111 1111 1111 1111 1111 1001 1100)转换为无符号整数时,会变成一个非常大的数:4294967196(补码原理)
// 然后与 n3 相加,结果是一个无符号整数
printf("n2 + n3 = %u,sizeof(n2 + n3) = %zu\n", n2 + n3, sizeof(n2 + n3)); // 输出:n2 + n3 = 4294967216,sizeof(n2 + n3) = 4
// 使用 %d 来输出
printf("n2 + n3 = %d\n", n2 + n3); // n2 + n3 = -80
在计算机的底层计算中,有符号整数通常使用补码表示,而无符号整数直接使用纯二进制表示。int n2 = -100; 中的字面量 -100 作为有符号数,其二进制补码表示为:1111 1111 1111 1111 1111 1111 1001 1100 。unsigned int n3 = 20; 中的字面量 20 作为无符号数,其二进制表示为:0000 0000 0000 0000 0000 0000 0001 0100 。
1111 1111 1111 1111 1111 1111 1001 1100
+ 0000 0000 0000 0000 0000 0000 0001 0100
-----------------------------------------
1111 1111 1111 1111 1111 1111 1011 0000
所以 -100 +20 得到的结果的二进制表示为:1111 1111 1111 1111 1111 1111 1011 0000。
如果使用格式占位符 %u 输出,则得到的无符号整数为:4,294,967,216
如果使用格式占位符 %d 输出,则得到的有符号整数为:-80
提示:
掌握补码的计算规则对于理解计算机底层的计算非常重要。
1.2 赋值时的自动类型转换
1.2.1 转换规则
在赋值运算中,赋值号两边量的数据类型不同时,等号右边的类型将转换为左边的类型。
如果窄类型赋值给宽类型,不会造成精度损失;
如果宽类型赋值给窄类型,可能会发生数据丢失,特别是当宽类型包含小数部分或超出窄类型的范围时。在这种情况下,小数部分会被截断,只保留整数部分。如果宽类型的值超出了窄类型的范围,还可能发生溢出,导致不可预测的结果。
1.2.2 案例演示
#include <stdio.h>
int main()
{
// 1. 窄类型赋值给宽类型
// 这里,int 类型(窄类型)的变量 narrowInt 被赋值给 double 类型(宽类型)的变量 wideDouble
// 不会发生数据丢失,因为 double 有足够的精度和范围来存储 int 的值
int narrowInt = 10;
double wideDouble = narrowInt;
printf("%f\n", wideDouble); // 输出:10.000000,因为 narrowInt 的值被准确地转换成了 double 类型
// 2. 宽类型赋值给窄类型
// 这里,int 类型(宽类型)的变量 wideInt 被赋值给 short 类型(窄类型)的变量 narrowShort
// 不会发生数据丢失,因为 short 有足够的范围来存储 int 的值(前提是 int 的值在这个范围内)
int wideInt = 10;
short narrowShort = wideInt;
printf("%d\n", narrowShort); // 输出:10,没有变化
// 3. 宽类型赋值给窄类型
// 这里,double 类型(宽类型)的变量 wideDouble2 被赋值给 int 类型(窄类型)的变量 narrowInt2
// 这种情况可能会导致数据丢失,因为 int 可能没有足够的精度或范围来存储 double 的所有值
// 特别是当 double 包含小数部分或超出 int 的范围时
double wideDouble2 = 1.2;
int narrowInt2 = wideDouble2;
// 当 double 被赋值给 int 时,小数部分会被截断,只保留整数部分
printf("%d\n", narrowInt2); // 输出:1,因为 1.2 的小数部分被截断了
// 4. 宽类型赋值给窄类型
// 这里,double 类型(宽类型)的变量 wideDouble3 被赋值给 char 类型(窄类型)的变量 narrowChar
// 当 double 包含小数部分时,会先转换为整数,然后赋值给 char
// 如果超出 char 能表示的范围,会发生数据溢出
double wideDouble3 = 133.456; // 133 二进制低八位:1000 0101(-123的补码)
char narrowChar = wideDouble3;
printf("%d\n", narrowChar); // 输出:-123,发生数据溢出
return 0;
}
输出结果如下所示:
总结:
- 窄类型(如 int)赋值给宽类型(如 double)时,不会发生数据丢失,转换是安全的。
- 宽类型(如 double)赋值给窄类型(如 int)时,可能会发生数据丢失,特别是当宽类型包含小数部分或超出窄类型的范围时。在这种情况下,小数部分会被截断,只保留整数部分。如果宽类型的值超出了窄类型的范围,还可能发生溢出,导致不可预测的结果。
2 强制类型转换(显式转换)
2.1 介绍
隐式类型转换中的宽类型赋值给窄类型,编译器是会产生警告的,提示程序存在潜在的隐患,如果非常明确地希望转换数据类型,就需要用到强制(或显式)类型转换。
2.2 转换格式
(类型名)变量、常量或表达式
2.3 转换规则
当浮点数被显式转换为整数时,小数部分会被丢弃,只保留整数部分。
在进行数学运算时,如果操作数中有浮点数,那么整个运算的结果也是浮点数。
2.4 案例演示
#include <stdio.h>
int main()
{
double d1 = 1.934;
double d2 = 4.2;
// 单独转换 d1 和 d2 为 int 类型,然后相加
// 这里,d1 被截断为 1(因为小数部分被丢弃),d2 被截断为 4
// 然后这两个整数相加得到 5
int num1 = (int)d1 + (int)d2; // 结果是 5
// 先将 d1 和 d2 相加,然后将结果转换为 int 类型
// 这里,d1 + d2 = 6.134,然后将 6.134 截断为 6(因为小数部分被丢弃)
int num2 = (int)(d1 + d2); // 结果是 6
// 直接在表达式中进行浮点运算,然后将结果转换为 int 类型
// 这里,3.5 * 10 = 35.0(浮点数),6 * 1.5 = 9.0(浮点数)
// 然后 35.0 + 9.0 = 44.0,最后将 44.0 截断为 44
int num3 = (int)(3.5 * 10 + 6 * 1.5); // 结果是 44
// 打印结果
printf("num1=%d \n", num1); // 输出:num1=5
printf("num2=%d \n", num2); // 输出:num2=6
printf("num3=%d \n", num3); // 输出:num3=44
return 0;
}
输出结果如下所示:
2.5 应用场景:整除除法希望结果是浮点数
在进行数学运算时:
- 如果操作数中包含浮点数,运算结果将是浮点数。
- 如果所有操作数都是整型数据,运算结果将是整数,小数部分会被截断,即整数除法的结果是整数。
如果希望结果是小数形式,可以将其中一个操作数转换为浮点数。这可以通过强制类型转换来实现,如下例所示。
#include <stdio.h>
int main()
{
int i = 5;
// 由于 i 和 2 都是整数,这里的除法运算执行的是整数除法,结果会向下取整,小数部分被截断。
float j = i / 2;
printf("%f\n", j);
// 输出: 2.000000,注意这里虽然用 %f 打印,但 j 的实际值是整数 2.0(浮点表示)
/* 如果希望结果包含小数部分,可以通过将操作数之一转换为浮点数来实现 */
// 方法 1:显式地将整数变量 i 强制转换为浮点数 (float)
// 这样,除法运算就会在浮点数域中进行,保留小数部分
float k = (float)i / 2;
printf("%f\n", k); // 输出: 2.500000,正确反映了除法结果的小数部分
// 方法 2:将除数 2 明确指定为浮点数 2.0
// 当操作数之一为浮点数时,整数 i 会被隐式地提升为浮点数(通常是 double 类型,因为 2.0 是 double 类型的)
// 此时的除法运算在浮点数域中进行,结果也是浮点数(double 类型)
// 但由于我们将结果赋值给 float 类型的变量 m,所以会发生从 double 到 float 的隐式类型转换
float m = i / 2.0; // 注意:虽然 i 被隐式提升为 double,但赋值给 float 时会进行转换
printf("%f\n", m); // 输出: 2.500000,正确显示了小数部分
return 0;
}
输出结果如下所示:
3 数据溢出和回绕
在 C 语言中,数据溢出(overflow)和回绕(wrap-around)是指当数值运算的结果超出了其数据类型所能表示的范围时发生的现象。
3.1 数据溢出
3.1.1 整数溢出
整数溢出发生在整数运算的结果或存储的数据超出了其类型所能表示的范围时。
案例一:
在 C 语言中,signed char 类型的取值范围是 -128 到 127。尝试将一个超过这个范围的值赋给 signed char 类型的变量时,就会发生溢出。
// 尝试给 signed char 赋一个超过其范围的值
signed char sc = 128; // 在某些情况下,这可能会溢出到 -128
printf("Signed char after overflow: %d\n", sc); // -128
在上面例子中,首先字面量 128 默认是 int(32 位)数据类型,然后尝试将字面量 128 赋给 signed char(8 位),这里会发生一次隐式类型转换(int -> char),所以变量 sc 中存储的是字面量 128 二进制(0000 0000 0000 0000 0000 0000 1000 0000)的低八位:1000 0000,但是变量 sc 是有符号类型,所以:1000 0000 将表示一个负数(补码)。可以通过计算器求得数值:
也可以手动计算:我们知道 1111 1111 表示 -1 的补码,1000 0000 即表示 1111 1111 一直减一减到最后,即 8 位二进制能表示的负数最小值:-128(-2^7)。所以最终打印输出为:-128。
案例二:
在 C 语言中,short 类型的取值范围是 -32768 到 32767。尝试将一个超过这个范围的值赋给 short 型的变量时,就会发生溢出。
#include <stdio.h>
int main()
{
// short 能表示的范围:-32,768 (-2^15) 到 32,767 (2^15 - 1)
short sh= 32768; // 这个数超过能表示的范围了,会发生数据溢出
printf("sh=%hd\n", sh);
// 输出:sh=-32768
return 0;
}
在上面例子中,首先字面量 32768 默认是 int(32 位)数据类型,然后尝试将字面量 32768 赋给 short(16 位),这里会发生一次隐式类型转换(int -> short),所以变量 sh 中存储的是字面量 32768 二进制(0000 0000 0000 0000 1000 0000 0000 0000)的低十六位:1000 0000 0000 0000,但是变量 sh 是有符号类型,所以:1000 0000 0000 0000 将表示一个负数(补码)。可以通过计算器求得数值:
也可以手动计算:我们知道 1111 1111 1111 1111 表示 -1 的补码,1000 0000 0000 0000 即表示 1111 1111 1111 1111 一直减一减到最后,即 16 位二进制能表示的负数最小值:-32768(-2^15)。所以最终打印输出为:-32768。
结论:
对于有符号整数,溢出会导致结果从最大值 “回绕” 到最小值,或者从最小值 “回绕” 到最大值。
#include <stdio.h>
int main()
{
/* signed char 类型的取值范围是 - 128 到 127 */
/* 对于有符号整数,溢出会导致结果回绕到该类型能表示的最小值或最大值 */
signed char sc1 = -128; // 最小值
printf("Signed char after overflow: %d\n", sc1); // -128
signed char sc2 = -129;
printf("Signed char after overflow: %d\n", sc2); // 回绕到最大值 127
signed char sc3 = -130;
printf("Signed char after overflow: %d\n", sc3); // 回绕到 126
signed char sc4 = 127; // 最大值
printf("Signed char after overflow: %d\n", sc4); // 127
signed char sc5 = 128;
printf("Signed char after overflow: %d\n", sc5); // 回绕到最小值 -128
signed char sc6 = 129;
printf("Signed char after overflow: %d\n", sc6); // 回绕到 -127
signed char sc7 = 130;
printf("Signed char after overflow: %d\n", sc7); // 回绕到 -126
return 0;
}
输出结果如下所示:
3.1.2 浮点数溢出
浮点数在 C 语言中使用 IEEE 754 标准表示,这一标准极大扩展了数值的表示范围,允许表示非常大的数和非常小的数。然而,由于存储方式和精度的限制,当数值超出这一范围或接近但不足以精确表示时,浮点数会表现出特殊行为。这种行为与整数溢出不同,浮点数不会因数值过大而直接“溢出”到错误的值。具体来说:
- 对于超出表示范围的数,浮点数会变成无穷大(Infinity)。
- 对于接近零但不足以精确表示的小数,浮点数会经历下溢逐渐趋近于零(Underflow to zero)。
- 对于接近但精度不足的数,则会发生精度丢失,即部分信息无法被准确表示。
#include <stdio.h>
#include <math.h>
#include <float.h>
int main()
{
// 极大数示例
// DBL_MAX 定义了 double 类型的最大值
double largeNumber = DBL_MAX * 2; // 尝试创建一个大于 DBL_MAX 的数
printf("Large Number: %g\n", largeNumber); // 应该输出 "inf" 表示无穷大
// 极小数示例
double verySmallNumber = 1e-308 * 1e-308; // 创建一个非常小的数
printf("Very Small Number: %g\n", verySmallNumber); // 应该输出接近零的数或零
// 精度丢失示例
double precisionLossNumber = 0.10000001 + 0.20000002; // 理论上应该是 0.30000003,但由于精度问题可能不是
printf("Precision Loss Number: %g\n", precisionLossNumber); // 输出可能不是精确的 0.30000003
// 浮点数常量和无穷大
double infinity = 1.0 / 0.0; // 创建一个无穷大的数
printf("Infinity: %g\n", infinity); // 应该输出 "inf"
double negativeInfinity = -1.0 / 0.0; // 创建一个负无穷大的数
printf("Negative Infinity: %g\n", negativeInfinity); // 应该输出 "-inf"
// 浮点数下溢示例
double underflowNumber = 1e-324 * 1e-324; // 创建一个非常接近零但不足以表示的小数
printf("Underflow Number: %g\n", underflowNumber); // 应该输出接近零的数或零
return 0;
}
输出结果如下所示:
提示:
%g 是一个格式化输出的占位符,用于 printf 和 scanf 等函数中。它根据值的大小自动选择最合适的表示形式,即 e 形式(科学计数法)或 f 形式(固定小数点表示)。当数值非常大或非常小的时候,它会选择科学计数法(即 e 形式),而在其他情况下,它会选择固定小数点表示(即 f 形式)。
3.1.3 类型转换溢出
当将一种数据类型的值转换为另一种类型,并且目标类型的表示范围小于原类型时,如果原值超出了目标类型的范围,就会发生类型转换时的范围溢出或值截断。例如,将 int 类型的值转换为 char 类型时,如果 int 类型的值超出了 char 类型的范围,就会导致数据丢失或意外的行为。在 C/C++ 中,这种转换通常是隐式的(除非显式地进行了类型转换),并且结果可能会导致数据丢失或意外的行为。
#include <stdio.h>
#include <limits.h>
int main()
{
int intValue = 256; // int 类型可以存储比 256 大得多的值
char charValue;
// 将 int 类型的值转换为 char 类型
charValue = (char)intValue;
// 输出原始的 int 值和转换后的 char 值
printf("Original int value: %d\n", intValue); // 256
printf("Converted char value: %d\n", charValue); // 0
// 输出 CHAR_MAX 和 CHAR_MIN 以显示 char 类型的范围
printf("CHAR_MIN: %d\n", CHAR_MIN); // -128
printf("CHAR_MAX: %d\n", CHAR_MAX); // 127
return 0;
}
在上面例子中,首先字面量 256 是 int(32 位)数据类型,然后尝试将其强转成 char(8 位),所以变量 charValue 中存储的是字面量 256 二进制(0000 0000 0000 0000 0000 0001 0000 0000)的低八位:0000 0000,即为 0,所以输出打印 charValue 值为 0。
3.2 整数回绕
整数回绕通常发生在整数类型中,尤其是当一个整数超过了其类型的最大值或最小值时。
当尝试给一个无符号数据类型(如 unsigned char、unsigned short 或 unsigned int 等)赋予一个超出其表示范围的值时,并不会发生传统意义上的 “溢出”。相反,这个值会通过模运算(即取余运算)来得到一个有效的无符号值。这是因为无符号类型不区分正负,而是将超出范围的值通过模该类型能表示的最大值加 1 来得到一个有效的值。这种现象通常被称为 “回绕”。
当尝试给一个无符号数据类型(如 unsigned char、unsigned short 或 unsigned int 等)赋予一个负数时,这个过程实际上涉及到的是类型转换和二进制表示的转换,而不是直接的模运算。但可以通过回绕处理求出数值。
- 有符号整数回绕:当一个有符号整数超过其类型的最大值时,它会回绕到该类型的最小值。同样,当一个有符号整数小于其类型的最小值时,它会回绕到该类型的最大值。例如,int 类型的范围通常是 -2,147,483,648 到 2,147,483,647(对于 32 位整数)。如果 int 类型的值超过了 2,147,483,647,它会回绕到 -2,147,483,648;如果 int 类型的值小于 -2,147,483,648,它会回绕到 2,147,483,647。
- 无符号整数回绕:当一个无符号整数超过其类型的最大值时,它会回绕到 0。同样,当一个无符号整数赋值小于 0 时,尽管是错误做法,但是也有回绕效果,它会回绕到该类型的最大值。例如,unsigned int 类型的范围是 0 到 4,294,967,295(对于 32 位整数)。如果 unsigned int 类型的值超过了 4,294,967,295,它会回绕到 0。
案例一:
// 尝试给 unsigned char 赋一个超过其范围的值(这会导致模运算)
unsigned char uc = 257; // 这将模 256(255+1),uc 的值将变为 1
printf("Unsigned char after overflow: %u\n", uc); // 1
底层原理:在上面例子中,首先字面量 257 默认是 int(32 位)数据类型,然后尝试将字面量 257 赋给 unsigned char(8 位),这里会发生一次隐式类型转换(int ->char),所以变量 uc 中存储的是字面量 257 二进制(0000 0000 0000 0000 0000 0001 0000 0001)的低八位:000 0001,即为 1。
模运算处理:在 C 语言中,unsigned char 类型的取值范围是 0 到 255。尝试将一个超过这个范围的值赋给 unsigned char 类型的变量时,就会发生模运算。变量 uc 实际存储的值是 257 除以 256(该类型能表示的最大值加 1 :255+1) 的余数,为 1,所以变量 uc 的值为 1。
回绕处理:对于 unsigned char 类型,其取值范围是 0 到 255。如果尝试赋予一个超出这个范围的值,会发生回绕处理。例如,当给 unsigned char 赋值 257 时,实际上会回绕到 0,然后继续处理,因此 256 会回绕到 0,而 257 会回绕到 1。
案例二:
当给一个 unsigned 类型赋值一个负数时,由于 unsigned 类型不表示负数,这个负数会被当作一个大的正数来处理。
#include <stdio.h>
int main()
{
unsigned short sh = -20; // 给一个无符号整数赋一个负数值,
printf("sh=%hu\n", sh);
// 输出:sh=65516
return 0;
}
底层原理:在上面例子中,首先字面量 -20 默认是 int(32 位)数据类型,然后尝试将字面量 -20 赋给 unsigned short(16 位),这里会发生一次隐式类型转换(int -> unsigned short),所以变量 sh 中存储的是字面量 -20 二进制(1111 1111 1111 1111 1111 1111 1110 1100)的低十六位:1111 1111 1110 1100,但是变量 sh 是无符号类型,所以:1111 1111 1110 1100 将表示一个正数。可以通过计算器求得数值:
数值关系:当给一个 unsigned 类型的变量赋一个负数值时,这个负数值首先被转换为其二进制补码表示,然后这个补码被直接解释为一个无符号数。这个无符号数与原始负数在数值上的关系是:它们之和等于该 unsigned 类型能表示的最大值加 1(在这个例子中是 65536)。因此,如果无符号数加上 20(数值上的和,不管符号),应该等于 65536(即 65535+1),这意味着无符号数为 65516。
回绕处理:unsigned short 类型能表示的数据范围为 0 到 65535。对于 -20 这样的值,相当于从最小值 0 往左数 20 个数。左数一个数(-1)相当于回绕到最大值 65535,左数两个数(-2)相当于回绕到 65534,因此左数 20 个数(-20)相当于回绕到 65516。
#include <stdio.h>
int main()
{
// unsigned short 类型能表示的范围 0 ~ 65535
unsigned short sh1 = 0; // 最小值
printf("sh1=%hu\n", sh1); // 0
unsigned short sh2 = -1; // 回绕到最大值 65535
printf("sh2=%hu\n", sh2);
unsigned short sh3 = -2; // 回绕到 65534
printf("sh3=%hu\n", sh3);
unsigned short sh4 = -3; // 回绕到 65533
printf("sh4=%hu\n", sh4);
unsigned short sh5 = 65535; // 最大值
printf("sh4=%hu\n", sh5); // 65535
unsigned short sh6 = 65536; // 回绕到最小值 0
printf("sh4=%hu\n", sh6);
unsigned short sh7 = 65537; // 回绕到 1
printf("sh4=%hu\n", sh7);
return 0;
}
输出结果如下所示:
总结:
- 对于 signed 类型,超出范围的值会导致溢出,可能引发回绕到最小或最大值。
- 对于 unsigned 类型,超出范围的值会通过模运算得到一个有效的无符号值。
- 大多数现代编译器和硬件平台都会以可预测的方式(如回绕)处理数据的溢出。
- 底层原理还是需要掌握,如基本数据类型的存储大小、所占位数、数据表示范围、补码的运算等。
4 测试题
1. 变量 int a 和 short b ,现在 a 和 b 进行运算 a + b, 哪个变量会发生数据类型转换,转换为什么类型 ?
【答案】变量 b 会发生数据类型转换,转换为 int 类型。
【解析】运算过程中,窄类型整数自动转换为宽类型整数。
2. 如果将一个字节宽度较大的类型转为字节宽度较小的类型,可能会造成什么问题 ?
【答案】精度损失、数据溢出
【解析】宽类型(如 double)赋值给窄类型(如 int)时,可能会发生数据丢失,特别是当宽类型包含小数部分或超出窄类型的范围时。在这种情况下,小数部分会被截断,只保留整数部分,导致精度损失。如果宽类型的值超出了窄类型的范围,还可能发生溢出,导致不可预测的结果。