目录
前言
什么是隐式类型转换?什么时候会发生?那些类型可以隐式转换?强制类型转换什么情况必须用?c++与类型转换的4种关键字分别是什么,有什么区别和联系,各在什么场合下使用?等。
本文针对以上问题做简要小结。
同时会涉及到以下内容:
关于printf输出格式与类型不一致,请查看《%d输出float类型,%f输出int类型》
关于符号扩展,有符号与无符号的转换问题,请查看《符号位扩展,空间不足—char和int举例》
1. c的类型转换
1.1 隐式类型转换
隐式类型转换(自动类型转换)的发生:
一.借助"=“,因"="两边类型一致
二.函数调用,传参或返回时(实际上也是借助”=")
三.能够隐式转换的类型:
1.基本数据类型之间。
如:int和double(double->int:可能出现丢失精度的问题)
int和char(int->char:可能出现空间不足,取低位,丢失高位数据1;char->int:会符号位扩展1);
2.通用指针。因所有类型的指针都可转化为通用指针,通用指针也可转化为任意类型的指针
注意:输出格式与类型不一致,不会发生隐式类型转换!
如:printf(“%f,%d\n”, 1, 1.0);2
上代码:
#include<stdio.h>
int main() {
// 隐式类型转换(借助"=",因"="两边类型一致):
// double->int 丢失精度
// int->double
float PI = 3.14159;
int s1, r = 5;
double s2;
s1 = r * r * PI; // 隐式转换,double->int 丢失精度
s2 = r * r * PI;
printf("s1=%d, s2=%f\n", s1, s2);
int a = 5;
double a1 = a/2; // 隐式类型转化,int—>double
double a2 = 2; // 隐式类型转化,int—>double
double a3 = (double)2; // 强制类型转换,int->double;一般采取此种写法
double a4 = (double)a/2;
double a5 = a/2.0;
printf("a1=%f,a2=%f,a3=%f,a4=%f,a5=%f\n", a1, a2, a3, a4, a5);
double v = 5.0;
int v1 = 5/2;
int v2 = 5.0/2; // 隐式类型转换,double->int 丢失精度
int v3 = 2.5; // 隐式类型转换,double->int
int v4 = (int)2.5; // 强制类型转换,double->int 丢失精度
printf("v1=%d, v2=%d, v3=%d, v4=%d\n", v1, v2, v3, v4);
printf("--------------------------------------------------------------------------\n");
// 隐式类型转换(借助"=",因"="两边类型一致):
// 任何类型的指针->void*
// void*->任何类型的指针
int c = 1;
int *p_int = &c;
void *p = p_int; // 通用指针,可以隐式转换
void *p2 = (void*)p_int; // 一般采取强制类型转化的写法
//p_double = p_int; // 不可以隐式转换,编译过不了
printf("%d\n", *(int*)p);
printf("----------------------------------------------------------------------------\n");
return 0;
}
1.2 强制类型转换
强制类型转换的格式为:(type_name) expression
在写法上,"="两边一般都是强制类型转化的写法(即便是可以隐式转换的类型,不写强转也没错,但不建议):
1.直观上看,两边类型一致,易读易懂
2.避免出错,因为隐式类型转换只发生在如上所述的某些类型之间,其余的类型必须用强制类型转换,不然编译出错
1.3 类型转换的函数
1.3.1 字符串转实型:atoi(),strtol()等
一般都是数字的字符串,如:“178666”->178666
atoi为代表的:
char* 字符串转换int
int atoi(const char* nptr);
char* 字符串转换long
long atol(const char* nptr);
char* 字符串转换long long
long long atoll(const char *nptr);
char* 字符串转换float
double atof(const char *nptr);
strtol为代表的:
char* 字符串转换 unsigned long int
unsigned long int strtoul(const char *nptr, char **endptr, int base);
char* 字符串转换 unsigned long long int
unsigned long long int strtoull(const char *nptr, char **endptr, int base);
char* 字符串转换 long int
long int strtol(const char *nptr, char **endptr, int base);
char* 字符串转换 long long int
long long int strtoll(const char *nptr, char **endptr, int base);
char* 字符串转换 double
double strtod(const char *nptr, char **endptr);
char* 字符串转换 float
float strtof(const char *nptr, char **endptr);
char* 字符串转换 long double
long double strtold(const char *nptr, char **endptr);
1.3.2 实型转字符串:sprintf()
1.3.3 网络字节序转换函数:ntohs(),htons()
ntohs()
#include <netinet/in.h>
简述:将一个无符号短整型数从网络字节顺序转换为主机字节顺序。(16位)。
输入uint16_t netshort:一个以网络字节顺序表达的16位数。
返回值:uint16_t ntohs返回一个16位以主机字节顺序表达的数。
uint16_t ntohs(uint16_t netshort);
htons()
#include <netinet/in.h>
uint16_t htons(uint16_t hostshort);
ntohl()
简述:将一个无符号长整形数从网络字节顺序转换为主机字节顺序。(32位)
输入uint32_t netlong:一个以主机字节顺序表达的32位数。
返回值:uint32_t ntohl返回一个32位以网络字节顺序表达的数。
#include <netinet/in.h>
uint32_t ntohl(uint32_t netlong);
htonl()
#include <netinet/in.h>
uint32_t htonl(uint32_t hostlong);
2. C++的类型转换
C++向下兼容C,故C的隐式类型转换,强制类型转换,和类型转换的一些函数在C++里同样适应。
C++特有的,4种与类型转换相关的关键字。用法小结:
1.const_cast
常量指针或常量引用去const属性
2.static_cast(可以进行比特位上的转换)
基本数据类型之间的转换
3.dynamic_cast(基类必须有虚函数)
安全的基类指针和派生类指针之间转换。
4.reinterpret_cast(不可以进行比特位上的转换)
指针类型的转换
上代码:
const_cast:
#include <iostream>
using namespace std;
// const_cast 去掉const属性:
// const_cast<int*> (&num),常用,因为函数调用时不能把一个const指针或应用直接赋给一个非const指针或引用,必须要转换。
// const_cast的目的并不是为了让你去修改一个本身被定义为const的值,因为这样做的后果是无法预期的。
// const_cast的目的是修改一些指针/引用的权限,如果我们原本无法通过这些指针/引用修改某块内存的值,现在你可以了。
int main() {
// const_cast的目的是修改一些指针/引用的权限
int a = 100;
const int *pi = &a;
int *pi2 = const_cast<int *>(pi);
*pi2 = 200;
cout << a << "," << *pi << "," << *pi2 <<endl; // res:200,200,200
printf("-----------------------------------------------------\n");
// const int b = 1;
// int b2 = const_cast<int>(b); //编译通不过,不允许
// 去修改一个本身被定义为const的值,因为这样做的后果是无法预期的。
const int c = 100;
const int *p = &c;
int *p2 = const_cast<int *>(p);
*p2 = 200;
cout << c << "," << *p << "," << *p2 <<endl; // res:100,200,200
return 0;
}
static_cast:
// static_cast 静态类型转换。用于:
// 1. 基本数据类型转换。enum, struct, int, char, float等。
// 2. static_cast不能进行无关类型(如非基类和子类)指针之间的转换,通用指针void*可以。
// 3. 基类和子类之间转换:其中子类指针转换成父类指针是安全的;
// 但父类指针转换成子类指针是不安全的。(基类和子类之间的动态类型转换建议用dynamic_cast)
int main() {
int n = 6;
int *pn = &n;
double d = static_cast<double>(n); // 基本类型转换
//double *d2 = static_cast<double*>(&n); // 无关类型指针转换,编译错误
void *p = static_cast<void*>(pn); // 任意类型转换成void类型
}
dynamic_cast:
// dynamic_cast
// 有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL):
// 1. 安全的基类指针和派生类指针之间转换。
// 2. 必须要有虚函数。
class BaseClass {
public:
int m_iNum;
virtual void foo(){}; // 基类必须有虚函数。保持多态特性才能使用dynamic_cast
};
class DerivedClass: public BaseClass {
public:
char*m_szName[100];
void bar(){};
};
int main() {
DerivedClass * pb = new DerivedClass();
BaseClass *pd1 = static_cast<BaseClass *>(pb); // 子类->父类,静态类型转换,正确但不推荐
BaseClass *pd2 = dynamic_cast<BaseClass *>(pb); // 子类->父类,动态类型转换,正确
BaseClass* pb2 =new BaseClass();
DerivedClass *pd21 = static_cast<DerivedClass *>(pb2); // 父类->子类,静态类型转换,危险!访问子类m_szName成员越界
DerivedClass *pd22 = dynamic_cast<DerivedClass *>(pb2); // 父类->子类,动态类型转换,安全的。结果是NULL
cout << pd21 << endl; // res:0x1e1aa0
cout << pd22 << endl; // res:0
// int a = 1;
// int *pa = &a;
// double *b = dynamic_cast<double*>(pa); //int*->double*,编译错误!
}
reinterpret_cast:
// reinterpret_cast
// 1. 转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。
// 2. 最普通的用途就是在函数指针类型之间进行转换。
// 3. 仅仅重新解释类型,它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。
// 但不会进行比特位上的转换,如:不能int->double
typedef void(*FuncPtr)();
int doSomething(){ return 0; };
int main() {
FuncPtr funcPtrArray[10];
//funcPtrArray[0] = &doSomething; // 编译错误!类型不匹配,reinterpret_cast可以让编译器以你的方法去看待它们:funcPtrArray
funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); // 不同函数指针类型之间进行转换
printf("---------------------------------------------------------\n");
int n = 6;
int *pn = &n;
double *d2 = reinterpret_cast<double*>(&n); // int*->double*
void *p = reinterpret_cast<void*>(pn); // int*->void*
printf("%d\n", *(int*)p);
printf("----------------------------------------------------------\n");
//double d = reinterpret_cast<double>(n); // int->double,编译错误!
//char c = 127;
//int d = reinterpret_cast<int>(c); // char->int,编译错误!
printf("%d\n", sizeof(int*)); // res:8
// 64bit故用long long
long long int p2 = reinterpret_cast<long long>(pn);
int *p3 = reinterpret_cast<int*>(10);
printf("llx:0x%llx\n", p2);
printf("llx:0x%llx\n", p3);
}