隐式/显式类型转换?编程语言的类型转换?其它类型转换成数值类型?其它类型转换成字符串?类型转换?
目录
隐式/显式类型转换
隐式转换
不同的显式类型转换
显示转换一定会成功?
数值和布尔类型转换
编程语言的类型转换
float类型变量任何时候都会转换成double类型吗?
强制类型转换
检测数据类型
其它类型转换成数值类型
转换成数值的API
其它类型转换成字符串
类型转换
隐式转换和强制转换
父类子类转换
枚举
浮点数和整数的转换
隐式/显式类型转换
编译器为了简化不同类型变量互操作,引入隐式转换。
隐式转换
- C/ObjC/C++/C# 等都支持隐式转换,"小"类型和"大"类型操作,"小"类型自动转换成"大"类型。但隐式转换很可能带来风险。
- 浮点数类型可以隐式转换成unsigned long long, 注意可能损失精度。
- Swift为了消除隐式转换的风险,规定不同类型变量运算,必须强制转换成相同类型
- 比如In8和Int32相加,Int8必须强制转换成Int32才能相加:a是Int8, b是Int32, b = Int32(a) + b
- 字面量运算不受如上限制,编译器会自动推导,例如var c = 1 + 0.1 - 仓颉 同样不支持隐式类型转换,对于二元运算符(除了幂运算符),两个操作数类型必须一致。包括但不限于算术运算符、关系运算符等。
不同的显式类型转换
- 仓颉
- Type(val)
例如Int64(3.5)
- Type(val)
- Swift
- Type(val)
- C/ObjC/C++/C#
- (Type)val
显示转换一定会成功?
- C# 显示转换只是程序员的一种想法,运行时遇到问题一样会抛出异常。
数值和布尔类型转换
- C/ObjC 布尔类型_Bool本质是整型,二者可以互相转换。
- C# 不允许数值和布尔互相转换。
编程语言的类型转换
- C/ObjC/C++
- 对于大小在int范围内的整型(包括char/short和枚举)的类型使用时都自动转换成int.
- 汇编示例:char和int类型变量相加,char类型会使用movsx符号位扩展为int大小再参与计算。
- int和unsigned运算,int隐式转换成unsigned int.
- 假设都是4字节,对于汇编代码,并不会有特别的指令区分int和unsigned,只会通过形如eax或edx寄存器计算。
- int/unsigned int和long类型运算,前者自动转换成long类型。
- 假设int是4字节,long是8字节。汇编语言可能通过cdqe指令自动将32位有符号数转换成64位(保持符号位不变)。但需要注意,形如int_val = int_val + long_val, 编译器可能不会遵循前面的规则,因为最终结果保存在int类型,右边表达式不一定会将int_val扩展为64位。
- float类型和double类型运算,float会自动转换成double类型参与运算。
- 汇编语言会使用cvtss2sd将单精度转换成双精度浮点数指令完成float自动转换成double.
- 大小在long范围内和double类型运算,前者自动转换成double类型。
- 大小在long范围内和float类型运算,前者自动转换成float类型。
- 对于大小在int范围内的整型(包括char/short和枚举)的类型使用时都自动转换成int.
float类型变量任何时候都会转换成double类型吗?
- 这是错误的!在C语言中,如果long类型和float运算,long类型转换成float一起参与运算,而不是都转换成double类型。float_val = long_val + float_val; 对应汇编会是cvtsi2ss指令将long类型转换成float类型参与计算,而不是转换成double类型的cvtsi2sd指令.
强制类型转换
- C/ObjC/C++/C#/Java/PHP 等支持用 (new_type) 强制转换已有类型的变量。
- PHP 还支持settype函数转换为特定类型,这也提现PHP动态语言的特征。
检测数据类型
- C/ObjC/C++ 不支持获取基本变量的类型(C++ typeid除外)。
- PHP 提供is_*系列函数获取变量的类型,例如:is_string('abc')
其它类型转换成数值类型
很多静态类型编程语言,不允许将非数值类型转换成数值类型,一些脚本语言为了简化复杂度,允许非数值类型按照特定规则转换成数值类型。
- JS
- Undefined类型 --> NaN.
- Null类型 --> 0.
- Boolean类型 true --> 1, false --> 0.
- String类型根据是十进制还是十六进制或者浮点数,转换成对应数值。注意,这里没有八进制数值,字符串"012"转换成数值是十进制的12,而不是八进制的012.
如果是空字符串或者多个空格,数值是0,如果是除了如上的其他字符串,即是非法数值,转换结果是NaN. - Object类型转换为数值经过2个步骤,先调用valueOf()函数,按照如上规则,有合法数值即返回,如果返回NaN, 继续调用toString()函数,同样按照如上规则得到数值,如果没有数值类型返回,返回NaN.
- C#
- 基本类型提供隐式转换或者强制转换。
- 字符串转换成基本类型:
- <Type>.Parse(<String>)
例如int.Parse("123") - <Type>.TryParse(<String>, out <Val>) (C# 2.0 ?)
和Parse的区别是,此函数转换失败不会抛出异常。
- <Type>.Parse(<String>)
- 字符串转换成基本类型:
- System.Convert.ToXXX(<String>)
例如ToInt, ToFloat, ...
- System.Convert.ToXXX(<String>)
- VB
- Val(str) 将str转换成数值,如果str包含不能转换为数值的字符,转换就此终止,返回已解析的数值。例如:
Val("a12") 返回 0,Val("-123ab") 返回 -123.
- Val(str) 将str转换成数值,如果str包含不能转换为数值的字符,转换就此终止,返回已解析的数值。例如:
转换成数值的API
- JS
Number函数用于将任何类型转换成数值。
parseInt函数和parseFloat函数都可以将字符串转换为数值。要特别注意,传入字符串和数值类型将导致结果不同,数值类型的参数会先转换为字符串再参与转换。例如parseInt(0x10, 16)和parseInt('0x10', 16)结果分别是22和16.
其它类型转换成字符串
大部分编程语言将一个变量转换成字符串,一般会经过toString方法或者类型转换方法,形如C语言的itoa.
- Swift提供一种极其优雅的方法,可以用 \(VAR) 做转换。
var i = 10
let str = "I have \(i) apples"
print(str)
- JS
- 基本类型
null --> "null", undefined --> "undefined" (注意不是空字符串), 布尔类型分别是"true"和"false", 字符串本身就返回自身,Number类型转换成对应的字符串,比如12 --> "12", 1.23 --> "1.23". - 引用类型:先调用toString函数,如果得到基本类型,按上面规则转换字符串;如果不满足,继续调用valueOf函数,如果还是基本类型,再次重复上面的转换;如果还不满足,抛出异常。
- 基本类型
- 仓颉
基本类型可调用toString()函数转换成字符串。 - C#
- 语言定义了基本类型ToString()方法,自定义类可以重写ToString()方法。
- VB
- Str(num) 函数可以将数值转换成字符串,num可以是整数或浮点数,不能是非数值字符。注意,如果num是正数,字符串会有前导空白字符。CStr(num) 函数基本和Str(num)一样,除了它在num是正数时不会有前导空白字符。
类型转换
编程语言中必然有很多情况需要转换类型。比如引入const的概念就为了提高安全性,编译器提前检查,避免一些意外修改。当然,有时,我们希望手动转换一个变量的类型,让其变成常量,可以利用编译器提供的cast方法。
- C++早期提供了const_cast,写法繁琐,C++17引入std::as_const写法更轻松,C++20引入bit_cast可实现位级别复制,而非普通float和int转换后丢失小数位的问题。
隐式转换和强制转换
编译器为了简化不同类型变量互操作,引入隐式转换,可以安全转换(不损失数据)数据,此过程由编译器自动完成。当不满足如上条件,需要程序员手动用强制类型转换。
- C/ObjC/C++ 语言大小小于int的整型(包括char和枚举)的类型使用时都自动转换成int. 有人有疑问,这样不是占用空间变大吗?事实上,仅仅是使用时,数据保存依然按对应类型。为什么使用时要转换成int呢?因为CPU操作int最自然,换成char或short一样会使用int大小的寄存器,没好处。下图push eax保存字符c,并传递给printf做参数。
- Swift 是一门相对安全的语言,没有隐式类型转换。
父类子类转换
子类本身就是父类的一种,转换成父类很自然,但父类转换成子类就需要考虑对不对。
- C++提供dynamic_cast主要为了继承体系转换,比static_cast更智能。虽然C++支持强制转换,必要时也可以通过typeid判断类型是否一致。
- Java默认也不允许父到子转换,但如果确定父对象引用确实是子对象,可用强制类型转换。
- C#不提供基类强转成派生类的方法。
- Python提供了简单的转换方式type(<Derived Class>)(<Base Class>).
- JS ES6虽然引入了类,一般无必要做父类子类转换。
- Go/Rust并不是纯粹的OOP语言,需要做一些手脚。
枚举
- C/C++常规枚举可以和整形做隐式转换,C++11引入作用域为类的枚举,不允许此行为以加强安全性,但可以使用显式转换。
浮点数和整数的转换
- C/ObjC/C++/仓颉
浮点数转换成整数,会丢弃小数。
若文章对您有帮助,欢迎关注 程序员小迷 。助您在编程路上越走越好!
微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。
我是 程序员小迷 (致力于C、C++、C#、Android、iOS、Java、Kotlin、Objective-C、Swift、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。