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

目录

  • 前言
  • [1. c的类型转换](#1. c的类型转换)
    • [1.1 隐式类型转换](#1.1 隐式类型转换)
    • [1.2 强制类型转换](#1.2 强制类型转换)
    • [1.3 类型转换的函数](#1.3 类型转换的函数)
      • [1.3.1 字符串转实型:atoi(),strtol()等](#1.3.1 字符串转实型:atoi(),strtol()等)
      • [1.3.2 实型转字符串:sprintf()](#1.3.2 实型转字符串:sprintf())
      • [1.3.3 网络字节序转换函数:ntohs(),htons()](#1.3.3 网络字节序转换函数:ntohs(),htons())
  • [2. C++的类型转换](#2. C++的类型转换)

前言

什么是隐式类型转换?什么时候会发生?那些类型可以隐式转换?强制类型转换什么情况必须用?c++与类型转换的4种关键字分别是什么,有什么区别和联系,各在什么场合下使用?等。

本文针对以上问题做简要小结。

同时会涉及到以下内容:

关于printf输出格式与类型不一致,请查看《%d输出float类型,%f输出int类型》

关于符号扩展,有符号与无符号的转换问题,请查看《符号位扩展,空间不足---char和int举例》


1. c的类型转换

1.1 隐式类型转换

隐式类型转换(自动类型转换)的发生:

一.借助"=",因"="两边类型一致

二.函数调用,传参或返回时 (实际上也是借助"=")

三.能够隐式转换的类型:

1.基本数据类型之间

如:int和double(double->int:可能出现丢失精度 的问题)

int和char(int->char:可能出现空间不足,取低位,丢失高位数据 [1](#1);char->int:会符号位扩展 [1](#1));

2.通用指针。因所有类型的指针都可转化为通用指针,通用指针也可转化为任意类型的指针

注意:输出格式与类型不一致,不会发生隐式类型转换

如:printf("%f,%d\n", 1, 1.0); [2](#2)

上代码:

bash 复制代码
#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:

bash 复制代码
#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:

bash 复制代码
// 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:

c++ 复制代码
// 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:

c++ 复制代码
// 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类型》 ↩︎

相关推荐
WBluuue1 小时前
数据结构与算法:dp优化——优化尝试和状态设计
c++·算法·leetcode·动态规划
java 乐山1 小时前
c 写一个文本浏览器(1)
c语言·开发语言
windows_61 小时前
MISRA C:2025 规则逐条分析
c语言·开发语言
睡不醒的kun2 小时前
定长滑动窗口-基础篇(2)
数据结构·c++·算法·leetcode·职场和发展·滑动窗口·定长滑动窗口
小王努力学编程2 小时前
LangChain——AI应用开发框架(核心组件1)
linux·服务器·前端·数据库·c++·人工智能·langchain
txzrxz2 小时前
单调栈详解(含题目)
数据结构·c++·算法·前缀和·单调栈
Trouvaille ~2 小时前
【Linux】进程间通信(二):命名管道与进程池架构实战
linux·c++·chrome·架构·进程间通信·命名管道·进程池
HellowAmy3 小时前
我的C++规范 - 随机时间点
开发语言·c++·代码规范
郝学胜-神的一滴3 小时前
深入解析C/S架构与B/S架构:技术选型与应用实践
c语言·开发语言·前端·javascript·程序人生·架构
啊阿狸不会拉杆3 小时前
《计算机操作系统》第七章 - 文件管理
开发语言·c++·算法·计算机组成原理·os·计算机操作系统