类型转换—c、c++总结

本文总结了C语言和C++中的类型转换,包括隐式类型转换、强制类型转换及其函数,如atoi()、strtol()等。还介绍了C++特有的4种类型转换关键字:const_cast、static_cast、dynamic_cast和reinterpret_cast。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

什么是隐式类型转换?什么时候会发生?那些类型可以隐式转换?强制类型转换什么情况必须用?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);
}

  1. 《符号位扩展,空间不足—char和int举例》 ↩︎ ↩︎

  2. 《%d输出float类型,%f输出int类型》 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值