本篇将会介绍static_cast,这种转换方式和以前学习的隐式转换很相似,基本等价于隐式转换的一种类型转换运算符,可使用于需要明确隐式转换的地方。

1. 隐式转换

在C语言中,存在不通过强制转换就可以将类型进行转换的隐式转换。
例子:

int main() {
	int n = 5;
	float f = 10.0f;
	
	//本质上发生了隐式转换,将int类型的5转换为float类型
	f = n;

	return 0;
}

运行结果:
在这里插入图片描述

2. 计算机数据存储方式:大小端

参考地址:大小端和指针的关系

2.1 大小端简介

计算机数据存储有两种字节优先顺序:高位字节优先(称为大端模式)和低位字节优先(称为小端模式)。

  • 大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。

  • 小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。

例子:对于内存中存放的数0x12345678来说
如果是采用大端模式存放的,则其真实的数是:0x12345678
如果是采用小端模式存放的,则其真实的数是:0x78563412

上面数字的存放方式示意图如下:
在这里插入图片描述

2.2 指针和大小端的结合使用

如果是用一个指针指向存储的这块内存,不管是大端还是小端,指向的都是这块内存的最地位地址。正是由于指向的是最低位地址,正好可以使用这个特性,来检查机器是大端还是小端。

int _tmain(int argc, _TCHAR* argv[])
{
	//int a[2] = {16909060,84281096};
	int a[2] = {0x01020304,0x05060708};
	char *pa =(char*) &a;

	int i = 8;
	while(i> 0)
	{
		printf("%02x  ",*pa);
		pa++;
		i--;
	}
	printf("\n");
}

输出结果为,证明机器为小端
在这里插入图片描述

2.3 大小端的读取过程

在这里插入图片描述

  • 读数据永远是从低地址开始的
  • 发送数据是从低地址开始发送
  • 收到数据是从低地址开始存储

我们知道这是小端存储,所以在读出来的时候会从低位开始放
我们知道这是大端存储,所以在读出来的时候会从高位开始放

2.4 大小端在网络中传输的细节

网络字节序是大端字节序

UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节存放在低地址处);由此可见,多字节数值在发送之前,在内存中因该是以大端法存放的;所以,在实际中传输中,当在两个存储方式不同的主机上传输时,需要借助字节序转换函数

如果小端向大端设备传送 0x 01 02 03 04 05 06 07 08 八个字节,假设是两个int
其在小端存储为
在这里插入图片描述
小端发送是从低地址开始读取,又由于发送的第一个字节需要是高位字节,所以需要网络序转换函数htonl将两个int转换成大端序,如下图,发送的顺序为 01 02 03 04 …… 08

在这里插入图片描述
大端接收到数据,把收到的第一个自己当做高位,即收到的数据如下图
在这里插入图片描述
按照两个字节读取,所以收到的第一个int是 0x01020304 第二个int是 0x05060708,正确

2.5 此处数据存储方式

数据以浮点编码格式,小端序进行保存
在这里插入图片描述

3. static_cast

对于上面的隐式转换还是需要人为的去思考转换类型,C++提供了关键字static_cast来明确正在进行隐式转换。

写法如下:

	//static_cast作用等价于隐式转换,但是更加明确
	f = static_cast<float>(n);

基本等价于隐式转换的一种类型转换运算符,可使用于需要明确隐式转换的地方。简单一些的理解就是:将隐式转换显式化表示出来

3.1 可以与不可以转换的情况

  • 可以用于低风险的转换:
    整型与浮点型、字符与整形、转换运算符、空指针转换为任何目标类型的指针

  • 不可以用与风险较高的转换:
    不同类型的指针之间互相转换、整型和指针之间的互相转换、不同类型的引用之间的转换


#include <iostream>
#include <string>

class CInt {
public:
	//对int的转换运算符
	operator int() {
		return m_nInt;
	}

	int m_nInt;
};

int main() {
	int n = 5;
	float f = 10.0f;
	double dbl = 1.0;
	
	//本质上发生了隐式转换,将int类型的5转换为float类型
	//但是代码上写的不够明确
	f = n;

	//static_cast作用等价于隐式转换,但是更加明确
	f = static_cast<float>(n);

	//低风险的转换
	//整型与浮点型
	n = static_cast<int>(dbl);

	//字符与整型
	char ch = 'a';//占用一个字节
	n = static_cast<int>(ch);

	//void*指针的转换
	void* p = nullptr;
	int* pN = static_cast<int*>(p);

	//转换运算符的方式
	CInt nObj;
	//若没有前面的,这里会把nObj当做对象无法转换
	//等价于int kk=nObj
	int k = static_cast<int>(nObj);

	//高风险的转换
	int kk;
	char* p;
	//整型与指针类型的转换
	p = kk;//不可以转换
	char* k = static_cast<char*>(kk);//不可以转换

	//不同指针类型的转换
	int* pK;
	char* k = pK;//不可以转换
	char* k = static_cast<char*>(pK);//不可以转换

	return 0;
}

3.2 基类与派生类之间的转换

实现结果如下:


#include <iostream>
#include <string>

//基类与派生类之间的转换

class CFather
{
public:
	CFather() {
		m_nTest = 3;
	}

	virtual void foo() {
		std::cout << "CFather()::void foo()" << std::endl;
	}

	int m_nTest;
};

class CSon : public CFather
{
	virtual void foo() {
		std::cout << "CSon::void foo()" << std::endl;
	}
};

int main() {
	CFather* pFather = nullptr;
	CSon* pSon = nullptr;

	//父类转子类,不安全,子类可能包含的内容更多
	//pSon = pFather;//无法通过
	//pSon = static_cast<CSon*>(pFather);//可以通过,但是不安全,没有运行时的检测

	//子类转父类,安全,子类包含的内容要大于子类
	//pFather = pSon;//可以通过
	//pFather = static_cast<CFather*>(pSon);//可以通过
}

4.学习视频地址:强制转换static_cast

5.学习笔记:强制转换static_cast学习笔记

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐