C语言入门系列:数据类型转换

在这里插入图片描述

当不同数据类型的数据出现在同一个表达式中时,就会涉及数据类型转换,C语言中的数据类型转换有两种:

  • 自动类型转换
  • 强制类型转换

类型转换可能是安全的,即不会丢失数据;也可能是不安全的,即出现丢失数据的情况。

窄类型转换为宽类型,是安全的,不会丢失数据。

宽类型转换为窄类型,是不安全的,虽然不是百分百丢失数据,但有丢失数据的可能。

一,自动类型转换

自动类型转换是指在特点情况下,编译器将一种数据类型自动转换为另一种类型,自动类型转换可能是安全的,也可能是不安全的,即可能出现数据丢失。

1,赋值运算

赋值运算符左右两边的数据类型不一致时,会以变量的类型为准,将右边的值转成变量的类型。

1.1,浮点数赋值给整型变量-不安全

浮点数赋予整型变量时,C语言的转换过程简单粗暴,保留整数部分,丢弃小数部分。

int x = 3.14159627;

如上,变量x被声明为整型,然后赋一个double类型值。

编译器会把3.14159627转为整形,小数部分0.14159627会被丢弃(注意,不是四舍五入),保留整数3,3被赋值给变量x,因此变量x的值是3。

显然,这种情况下的自动类型转换导致数据丢失,是不安全的类型转换,我们在编写代码时,要避免类似的赋值语句,左右两边的类型一致才是最佳实践。

1.2,整数赋值给浮点数变量-安全

整型赋值给浮点数变量时,会自动转为浮点数。

float y = 12 * 2;

上面示例中,变量y的值不是24,而是24.0,因为等号右边的整数自动转为了浮点数。

C语言中的浮点数遵循了IEEE 754标准,使用科学计数法来存储浮点数,其数组范围被整形要大得多,所以整形赋值给浮点数,不会出现数据丢失,是安全的自动类型转换。

1.3,窄类型赋值给宽类型-安全

字节宽度较小的整数类型,赋值给字节宽度较大的整数变量时,会发生类型提升,即窄类型自动转为宽类型。

比如,char或short类型赋值给int类型,会自动提升为int。

char x = 100;
int i = x + y;

上面示例中,变量x的类型是char,由于赋值给int类型,所以会自动提升为int。

1.4,宽类型赋值给窄类型-不安全

字节宽度较大的类型,赋值给字节宽度较小的变量时,会发生类型降级,自动转为后者的类型。这时可能会发生截断,系统会将移除的高位二进制,从而出现意料之外的情况。

int i = 321;
char ch = i; // ch 的值是 65 (321 % 256 的余值)

上面例子中,变量ch是char类型,宽度是8个二进制位。

变量i是int类型,将i赋值给ch,后者只能容纳i(二进制形式为101000001,共9位)的后八位,前面多出来的二进制位被丢弃,保留后八位就变成了01000001(十进制的65,相当于字符A)。

之前介绍的浮点数赋值给整型变量,也属于宽类型自动转换为窄类型,也会发生截断,丢弃小数部分。

double pi = 3.14159;
int i = pi; // i 的值为 3

上面示例中,i等于3,pi的小数部分被截去了。

2,混合类型的运算

不同类型的值进行混合计算时,必须先转成同一个类型,才能进行计算。转换规则如下:

2.1,整形和浮点数混合

整数与浮点数混合运算时,整数转为浮点数类型,与另一个运算数类型相同。

3 + 1.2 // 4.2

上面示例是int类型与float类型的混合计算,int类型的3会先转成float的3.0,再进行计算,得到4.2。

2.2,不同的浮点数类型混合

运算时,宽度较小的类型转为宽度较大的类型,比如float转为double,double转为long double。

2.3,不同的整数类型混合

运算时,宽度较小的类型会提升为宽度较大的类型。

比如short转为int,int转为long等,有时还会将带符号的类型signed转为无符号unsigned。

下面例子的执行结果,可能会出人意料。

int a = -5;
if (a < sizeof(int)do_something();

上面示例中,变量a是带符号整数,sizeof(int)是size_t类型,这是一个无符号整数。

按照规则,signed int 自动转为 unsigned int,所以a会自动转成无符号整数4294967291(转换规则是-5加上无符号整数的最大值,再加1),导致比较失败,do_something()不会执行。

所以,最好避免无符号整数与有符号整数的混合运算。因为这时 C 语言会自动将signed int转为unsigned int,可能不会得到预期的结果。

3,整数类型的运算

两个相同类型的整数运算时,或者单个整数的运算,一般来说,运算结果也属于同一类型。

但是有一个例外,宽度小于int的类型,运算结果会自动提升为int。

unsigned char a = 66;

if ((-a) < 0) printf("negative\n");
else printf("positive\n");

上面示例中,变量a是 unsigned char 类型,这个类型不可能小于0,但是-a不是 unsigned char 类型,会自动转为 int 类型,导致上面的代码输出 negative。

再看下面的例子。

unsigned char a = 1;
unsigned char b = 255;
unsigned char c = 255;

if ((a - 5) < 0) do_something();
if ((b + c) > 300) do_something();

上面示例中,表达式a - 5和b + c都会自动转为 int 类型,所以函数do_something()会执行两次。

4,函数

函数的参数和返回值,会自动转成函数定义里指定的类型。

int dostuff(int, unsigned char);

char m = 42;
unsigned short n = 43;
long long int c = dostuff(m, n);

上面示例中,参数变量m和n不管原来的类型是什么,都会转成函数dostuff()定义的参数类型。

下面是返回值自动转换类型的例子。

char func(void) {
  int a = 42;
  return a;
}

上面示例中,函数内部的变量a是int类型,但是返回的值是char类型,因为函数定义中返回的是这个类型。

二,强制类型转换

最佳实践是,我们在编写代码时,应该避免自动类型转换,因为自动类型转换可能导致出现意料之外的情况。

代码的行为始终在程序员的预料之中,是程序员必须追求的目标。

对于必不可少的类型转换,最好是使用强制类型转换

强制类型转换是指在一个值或变量的前面,使用圆括号指定类型(type),称之为casting。

(unsigned char) ch

上面示例将变量ch转成无符号的字符类型。

char c = (char)266;

上面示例中,(char)将266强制转换为char类型。

首先,虽然从语法上看,这种转换是没有必要的,因为对于赋值运算来说,编译器会把右边的值自动转换为左边的类型。但是,这样的代码是我们推荐的写法,更直观,便于阅读。

其次,把整型266强制转换为char类型,会出现数据截断,导致部分数据丢失,但由于这是我们意料之中的情况,就不会出现安全问题。

  • 33
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小手追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值