利用汇编挖掘C++本质以及C++补充

2 篇文章 0 订阅
汇编的用途
  • 掌握编程语言,代码的本质
  • 破解
汇编

汇编语言与机器语言一一对应,每一条机器语言指令都有与之对应的汇编指令
高级语言可以通过编译得到汇编语言/机器语言,但汇编语言/机器语言几乎可能还原成高级语言
Visual studio 在调试下右键转汇编

9: 	int arrray[] = { 1,2,3 }; //C++语言
//汇编-   地址  机器指令 汇编代码
00007FF7BA87191C C7 45 08 01 00 00 00 mov         dword ptr [arrray],1  
00007FF7BA871923 C7 45 0C 02 00 00 00 mov         dword ptr [rbp+0Ch],2  
00007FF7BA87192A C7 45 10 03 00 00 00 mov         dword ptr [rbp+10h],3  

高级语言转换过程

  • 编译型语言(不依赖虚拟机)

C\C++\OC\Swift

  • 脚本语言

Python\JS\PHP

  • 编译型语言(依赖虚拟机)

Java\Ruby

Visual studio 2022常用快捷键

常用快捷键

为什么C++支持函数重载

采用name mangling 或者name decoration技术

  • C++编译器默认会对符号名(比如函数名)进行改变,修饰
  • 重载时会生成多个不用的函数名,不同编译器(MSVC,g++)有不同的生成规则
默认参数
  • 默认参数必须从右边开始
  • 如果同时有声明,实现,默认参数只能放在函数声明中
指针存储函数名
void func(int v1,void(*p)(int)) //函数指针
{
	p(v1);
}
void test(int a)
{
	cout << "test(int) - "<< a <<endl;
}
int main(void)
{
	void(*p)(int) = test; //指针存储函数名
	p(10); 
	func(20,test);
}
extern “C”

被extern "C"修饰的代码会按照C语言的方式去编译,用在C和C++混合开发

#include  <iostream>
using namespace std;
extern "C" void func() //使用C编译函数重载
{

}
extern "C" void func(int v)
{

}
//或者
extern "C"  //使用大括号C编译内部
{
	void func()
	{

	}
	void func(int v)
	{

	}
}
int main()
{

	return 0;
}
//“func”: 无法重载具有外部 "C" 链接的

如果函数同时有声明和实现,要让函数声明被extern "C"修饰,函数实现可以不修饰(声明和实现都放,或者放声明处)

cpp使用C语言开源库
//math.c文件
int sum(int v1, int v2)
{
	return v1 + v2;
}
int delta(int v1, int v2)
{
	return v1 - v2;
}
#include  <iostream>

using namespace std;
//或者
extern "C"  //使用c来编译c函数
{
	int sum(int v1, int v2);
	int delta(int v1, int v2);
}
int main()
{
	cout << sum(10, 20) << endl;
	cout << delta(30, 20) << endl;

	return 0;
}
/*
输出
30
10
*/
C文件写入头文件
//math.h
#pragma once
int sum(int v1, int v2)
{
	return v1 + v2;
}
int delta(int v1, int v2)
{
	return v1 - v2;
}
#include  <iostream>

using namespace std;
//或者
extern "C"  //使用c来编译头文件
{
#include "math.h" 
}
int main()
{
	cout << sum(10, 20) << endl;
	cout << delta(30, 20) << endl;

	return 0;
}
/*
输出
30
10
*/
宏定义
  • C++默认会定义宏#define _cplusplus,将此应用到都文件中判断是否为C++环境来决定是否用C编译
#pragma once

#ifdef _cplusplus
extern "C"
{
#endif //_cplusplus

	int sum(int v1, int v2)
	{
		return v1 + v2;
	}
	int delta(int v1, int v2)
	{
		return v1 - v2;
	}

#ifdef _cplusplus
}
#endif //_cplusplus
#pragma once
  • 我们经常使用#ifndef,#define,#endif来防止头文件的内容被重复包含
  • #pragma once可以防止整个文件的内容被重复包含
内联函数与宏
  • 内联函数和宏,都可以减少函数调用的开销
  • 对比宏,内联函数多了语法检测和函数特性
C++有些表达式可以被赋值
int a = 1;
int b = 2;
//赋值给了a
(a = b) = 3;
//赋值给了 b
(a < b ? a : b) = 4;
const
//const的含义,const修饰的是右边的内容
int age = 10;
const int *p1 = &age;
//修饰*p1是常量,而p1地址可以改
int const *p2 = &age; //同p1,const可以和类型互换位置
int * const p3 = &age;
//const修饰右边的内容p3是地址常量,不变,而*p3可以修改
const int * const p4 = &age;
int const * const p5 = &age;
//p4和p5等价,地址和*p4,*p5都是常量不能变
引用
  • 引用的本质就是弱化了的指针
  • 一个引用占用一个指针的大小
  • 在C语言中,使用指针(Pointer)可以间接获取,修改某个变量的值
  • 在C++中,使用引用(Reference)可以起到指针类似的功能
    注意:
  • 引用相当于变量的别名(基本数据类型,枚举,结构体,类,数组等,都可以有引用)
  • 对引用做计算,就是对引用所指向的变量做计算
  • 在定义的事后就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”
  • 可以利用引用初始化另一个引用,相当于某个变量的多个别名
  • 不存在(引用的引用,指向引用的指针,引用数组)
  • 引用存在的价值:比指针更安全,函数返回值可以被赋值
  • const必须卸载&符号的左边,才能算常引用
const引用的特点
  • 可以指向临时数据(常量,表达式,函数返回值等)
  • 可以指向不同类型的数据
  • 作为函数参数时(此规则也适用于const指针)
  • 可以接受const和非cons实参(非const引用,只能接受非const实参)
  • 可以跟非const引用构成重载
  • 当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量
	int age = 10;
	const long& aAge = age;
	age = 30;
00007FF71556184D C7 45 04 0A 00 00 00 mov         dword ptr [rbp+4],0Ah  
	00007FF715561854 8B 45 04             mov         eax,dword ptr [rbp+4]  
	00007FF715561857 89 45 44             mov         dword ptr [rbp+44h],eax  
	00007FF71556185A 48 8D 45 44          lea         rax,[rbp+44h]  
	00007FF71556185E 48 89 45 28          mov         qword ptr [rbp+28h],rax  
	00007FF715561862 C7 45 04 1E 00 00 00 mov         dword ptr [rbp+4],1Eh  

#include  <iostream>
#include "math.h"
using namespace std;
/* 
两者引用:机器码相同,汇编相同
*/
int main()
{
	int age = 22;
	int* p = &age;
	*p = 25;
	cout << age << endl;
	int& ref = age;
	ref = 30;
	cout << age << endl;
	return 0;
}
 9: 	int& ref = age;
00007FF759091914 48 8D 45 04          lea         rax,[rbp+4]
00007FF759091918 48 89 45 28          mov         qword ptr [rbp+28h],rax
	10: 	ref = 30;
00007FF75909191C 48 8B 45 28          mov         rax,qword ptr [rbp+28h]
00007FF759091920 C7 00 1E 00 00 00    mov         dword ptr [rax],1Eh
   11: 	int* p = &age;
00007FF6CD505214 48 8D 45 04          lea         rax,[rbp+4]  
00007FF6CD505218 48 89 45 28          mov         qword ptr [rbp+28h],rax  
    12: 	*p = 25;
00007FF6CD50521C 48 8B 45 28          mov         rax,qword ptr [rbp+28h]  
00007FF6CD505220 C7 00 19 00 00 00    mov         dword ptr [rax],19h 

常引用数组
//数组名arr起始就是数组的地址,也是数组首元素的地址
// 数组名arr可以看做是指向数组首元素的指针(int *)
int arr[] = {1,2,3};
// int (&ref)[3] = arr;
int * const &ref = arr; //常量要用常引用
汇编语言
汇编语言的种类
  • 8086汇编(16bit)
  • x86汇编(32bit)
  • x64汇编(64bit)
  • ARM回避那(嵌入式,移动设备)
  • x64汇编根据编译器不同,两种书写格式

Intel:
AT&T

AT&T汇编 VS Intel汇编

image-20230302073341484

寄存器与内存

假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间

  • cpu首先会将红色内存空间的值放到EAX寄存器中:mov eax, 红色内存空间
  • 然后让eax寄存器与1相加: add eax,1
  • 最后将值赋值给内存空间:mov蓝色内存空间,eax
	int a = 5;
	a = a + 1;
    10: 	int a = 5;
00007FF6E7DA520D C7 45 04 05 00 00 00 mov         dword ptr [rbp+4],5  
    11: 	a = a + 1;
00007FF6E7DA5214 8B 45 04             mov         eax,dword ptr [rbp+4]  
00007FF6E7DA5217 FF C0                inc         eax  
00007FF6E7DA5219 89 45 04             mov         dword ptr [rbp+4],eax 
x64寄存器Register
  • RAX(包含EAX)\REX\RCX\RDX:通用寄存器
  • 32bit:EAX\EBX\ECX\EDX:通用寄存器
  • 16bit:AX\BX\CX\DX:通用寄存器
  • x64一个寄存器8个字节
  • R开头的寄存器是64bit的,占8字节
  • E开头的寄存器是32bit的,占4个字节
C++内联汇编
#include <iostream>
using namespace std;

int main()
{
	int a = 10;
	//双下划,C++嵌入汇编执行
	__asm
	{
		mov eax, 10
	}
}
x86汇编重要指令
  • mov dest,src

将src的内容赋值给dest,类似于dest = src

  • [地址值]
  • 中括号[]里面放的都是内存地址
* `word`是2字节,`dword`是4字节(double word),`qword`是八字节(quad word)
  • call 函数地址
  • 调用函数,并不是真实地址

  • CPU大小端模式,大部分都是小端模式
    小端模式:高高低低,高字节放高地址,低字节放低地址
    一个变量的地址值,是它所在字节地址中的最小值

  • lea dest, [地址值]

将地址赋值给dest,类似dest = 地址值

  • ret

函数返回

  • xor op1 , op2

op1op2``异或的结果赋值给op1,类似于op1 = op1 ^ op2

  • add op1,op2

类似于op1 = op1 + op2

  • sub op1,op2

类似于op1 = op1 - op2

  • inc op

自增,类似于 op = op + 1

  • dec op

自减,类似于op = op - 1

  • jmp 内存地址

    • 跳转到某个内存地址去执行代码
    • j开头的一般都是跳转,大多是带条件的跳转,一般跟testcmp等指令配合使用
      参考权威:Intel白皮书1
  • cmp eax , b

cmp是``compare的简称,比较eaxb`的值是否相等

跳转指令表格
JE, JZ结果为零则跳转(相等时跳转)ZF=1
equalzero
JNE, JNZ结果不为零则跳转(不相等时跳转)ZF=0
not equalnot zero
JS结果为负则跳转SF=1
sign(有符号\有负号)
JNS结果为非负则跳转SF=0
not sign(无符号\无负号)
JP, JPE结果中1的个数为偶数则跳转PF=1
parity even
JNP, JPO结果中1的个数为偶数则跳转PF=0
parity odd
JO结果溢出了则跳转OF=1
overflow
JNO结果没有溢出则跳转OF=0
not overflow
JB, JNAE小于则跳转 (无符号数)CF=1
belownot above equal<
JNB, JAE大于等于则跳转 (无符号数)CF=0
not belowabove equal>=
JBE, JNA小于等于则跳转 (无符号数)CF=1 or ZF=1
below equalnot above<=
JNBE, JA大于则跳转(无符号数)CF=0 and ZF=0
not below equalabove>
JL, JNGE小于则跳转 (有符号数)SF≠ OF
littlenot great equal<
JNL, JGE大于等于则跳转 (有符号数)SF=OF
not littlegreat equal>=
JLE, JNG小于等于则跳转 (有符号数)ZF=1 or SF≠ OF
little equalnot great<=
JNLE, JG大于则跳转(有符号数)ZF=0 and SF=OF
not little equalgreat>
汇编符号说明

汇编符号说明

参考的C++变量名规范
  • 全局变量:g_
  • 成员变量:m_
  • 静态变量: s_
  • 常量: c_
类补充
  • struct默认public
  • class 默认private
如何利用指针间接访问所指向量的成员变量
  • 从指针中取出对象的地址
  • 利用对象的地址 + 成员变量的偏移量计算出成员变量的地址
  • 根据成员变量的地址访问成员变量的存储空间
  • 类函数默认会被赋值为ccccccc
  • cc -> int3 :起到断点的作用
  • 中断:interrupt
内存空间的布局

每个应用都有自己独立的内存空间

  • 栈空间

每调用一个函数就会给它分配一段连续的栈空间,等函数调用完毕后会自动回收这段栈空间
自动分配和回收

  • 堆空间

需要主动去申请和释放,在程序运行过程,为了能够自由控制内存的声明周期,大小,会经常使用堆空间的内存

  • 代码区(段)

用于存放代码

  • 全局区(数据段)

用于存放全局变量等

对初始内存初始化

memset函数是将较大的数据结构(比如对象,数组等)内存清零的比较快的方法

int *p = (int *)malloc(sizeof(int)*10);
//*p=0;//并不能全部清零,仅前四个字节
memset(p,0,40);//从p地址开始,连续个字节,都设置为0
Person person;
person.m_id = 1;
person.m_age = 20;
person.m_height = 180;
memset(&person,0,sizeof(person));  //赋值person为零
new的初始化
int *p1 = new int;      //未被初始化
int *p2 = new int();    //被初始化为0
int *p3 = new int(5);    //被初始化为5
int *p4 = new int[3];    //数组元素未被初始化
int *p5 = new int[3]();  //3个数组元素都被初始化为0
int *p6 = new int[3]{};  //3个数组元素都被初始化为0
int *p7 = new int[3]{ 5 } //数组首元素被初始化为5,其它元素被初始化为零
malloc不会调用类的构造&析构函数
#include <iostream>
using namespace std;
struct Person
{
	int m_age;

	Person()
	{
		cout << "Person()" << endl;
	}
	~Person()
	{
		cout << "释放Person()" << endl;
	}
};
int main(void)
{
	Person person; //会调用默认构造函数
	Person* p = new Person; //申请空间也会调用默认构造函数
	delete p;
	//malloc并不调用默认构造函数,
	Person* q = (Person*)malloc(sizeof(Person));
	free(q); //也不会调用析构函数
	return 0;
}
/*
Person()
Person()
释放Person()
释放Person()
*/
成员变量的初始化
  • 全局区默认初始化为0
  • 如果自定义了构造函数,除了全局区,其他内存空间的成员变量默认都不会被初始化,需要开发人员手动初始化。
#include <iostream>
using namespace std;

struct Person
{
	int m_age;
};
Person g_person;  //全局变量初始化为0
int g_age;  //全局变量默认初始化为 0
int main(void)
{
	//Person person; //堆空间:没有初始化成员变量

	//堆空间:没有初始化成员变量
	Person* p0 = new Person;
	//堆空间:成员变量初始化为0,//如果有构造函数则不进行初始化
	Person* p1 = new Person();
	cout << g_age << endl;
	cout << g_person.m_age << endl;
	//cout << person.m_age << endl;
	cout << p0->m_age << endl;
	cout << p1->m_age << endl;

	return 0;
}
/*输出:
0
0
-842150451
0
*/
构造函数的相互调用
struct Person
{
	int m_age;
	int m_height;
	Person() :Person(0,0)
	{
		//Person(0,0)//不可,此命令汇编申请临时对象,而非调用
	}
	Person(int age,int height):m_age(age),m_height(height)
	{
	
	}
}
call汇编机械码
  • call汇编机械码E8直接调用,写死
  • call汇编机械码FF间接调用,可变
调用多态性虚表的汇编过程
//调用run
Animal *cat = new Cat();
cat->run();
//ebp-8是指针变量cat的地址
//eax存储的是cat对象的地址
move eax,dword ptr [ebp-8]
//取出cat对象的最前面4个字节(虚表地址)给edx
mov edx, dword ptr [eax]
//跳出虚表的最前面4个字节,取出4个字节赋值给eax
//取出cat::run的函数调用地址给eax
mov eax ,dword ptr [edx+4]
//call cat::run
call eax
单例模式(简)

设计模式的一种,保证某个类永远只创建一个对象

  • 构造函数私有化
  • 定义一个私有的static成员变量指向唯一的那个单例对象
  • 提供一个公共的访问单例对象的接口
/* 安全位置小火箭*/
class Rocket
{
private:
    Rocket()
    {

    }
    ~Rocket()
    {

    }
    static Rocket *ms_rocket;
public:
    static Rocket *sharedRocket()
    {
        //这里要考虑多线程的安全
        if(ms_recket == NULL)
            {
                ms_rocket = new Rocket();
            }
        return ms_rocket;
    }
    static void deleteRocket()
    {
        if(ms_rocket != NULL)
        {
            delete ms_rocket;
            ms_rocket = NULL;
        }
    }
}
const成员
  • const成员:被const修饰的成员变量,非静态成员函数
  • const成员变量
    • 必须初始化(类内部初始化)可以在声明的时候直接初始化赋值
    • 非static的const成员变量还可以再初始化列表中初始化
  • const成员函数(非静态)
  • const关键字写在参数列表后面,函数的声明和实现必须带const
  • 内部不能修改非static成员函数
  • 内部只能调用const成员函数,static成员函数
  • 非const成员函数可以调用const成员函数
  • const成员函数和非const成员函数构成重载
  • 非const对象(指针)优先调用非const成员函数
  • const对象(指针)只能调用const成员函数,static成员函数
拷贝函数
  • 利用已经存在的car3对象创建一个car3新对象
  • car4初始化会调用拷贝构造函数
class Car
{

public:
	int m_price;
	int m_length;
	car(int price = 0, int length = 0):m_price(price),m_length(length)
	{
		cout<< "Car(int price = 0,int length = 0)"<<endl;
	}
	//拷贝构造函数(会默认生成浅拷贝构造函数)
	Car(const Car &car):m_price(car.m_price),m_length(car.m_length)
	{
		cout <<"Car(const Car &car)"<<endl;
		// m_price = car.m_price;
		// m_length = car.m_length;
	}
	
	void display()
	{
		cout<< "price = " << m_price << " , length="<<m_length << endl;
	}
}

Car car3(100,5);
Car car4(car3);
car4.display();

浅拷贝(shallow copy):指针类型的变量只会拷贝地址值
深拷贝(deep copy):将指针指向的内容拷贝到新的存储空间

class Car
{
	int m_price;
	char *m_name;
	void copy(const char*name = NULL)
	{
		if(name == NULL)
			return ;
	}
	//申请新的存储空间
	m_name = new char[strlen(name)+1] {};
	strcpy(m_name,name);
}
浅,深拷贝
  • 编译器默认的提供的拷贝是浅拷贝(shallow copy)
  • 将一个对象中所有成员变量的值拷贝到另一个对象
  • 如果某个成员变量是个指针,只会拷贝指针中存储的地址值,并不会拷贝指针指向的内存空间
  • 可能会导致堆空间多次free的问题
  • 如果需要实现深拷贝(deep copy),就需要自定义拷贝构造函数
  • 将指针类型的成员变量所指向的内存空间,拷贝到新的内存空间
对象类型的参数和返回值

使用对象类型作为函数的参数或者返回值,可能会产生一些不必要的中间对象,返回引用可避免中间值

匿名对象

匿名对象:没有变量名,没有被指针指向的对象,用完后马上调用析构

car();
//一次性对象,调用完之后立即释放空间
隐式构造/转换构造
Person p1 = 20;
//等同于
Person p2(20);
//都会调用单构造函数
//隐式构造
Person test2()
{
	return 40; //隐式构造,会将40传递给Person单函数对象
}

可以通过关键字explicit在定义构造函数时禁止掉隐式构造

编译器何时自动生成构造函数

virtual function table在汇编显示:vftable

  • 成员变量在声明的同时进行了初始化
  • 有定义虚函数
  • 虚继承其他类
  • 包含了对象类型的成员,且这个成员有构造函数(编译器生成或自定义)
  • 父类有构造函数(编译器生成或自定义)
  • 对象创建后,需要做一些额外操作时(比如内存操作,函数调用),编译器一般都会为期生成构造函数
内部类

如果将类A定义在类C的内部,那么类A就是一个内部类(嵌套类)

  • 内部类的特点
    • 支持public,protected,private权限
    • 成员函数可以直接访问其外部类对象的所有成员(反过来不行)
    • 成员函数可以直接不带类名,对象名访问其外部类的static成员
    • 不会影响外部类的内存布局
局部类

在一个函数内部定义的类,称为局部类

  • 局部域仅限于所在的函数内部
  • 其所有的成员必须定义在类内部,不允许定义static成员变量
  • 成员函数不能直接访问函数的局部变量(static变量除外)
  • 在函数内部定义的类如果未调用类和函数,并不会每次调用函数都调用类
类型转换
  • C语言风格类型转换符
    • (type)expression
    • type(expression)
int a = 10;
double d = (double) a;
double d2 = double(a);
  • C++中有4个类型转换符
    • static_cast
    • dynamic_cast
    • reinterpret_cast
    • const_cast
    • 使用格式:xx_cast(expression)
int a = 10;
double d = static_cast<double>(a);
const_cast

一般用于const属性,将const转换为非const

#include <iostream>
using namespace std;

class Person
{

};
int main(void)
{
	const Person* p1 = new Person();
	//实现去除const属性的强制类型转换
	Person* p2 = const_cast<Person*>(p1); 
	Person* p3 = (Person*)p1;
	//汇编语言相同

	return 0;
}
/*
	12: 	Person* p2 = const_cast<Person*>(p1);
00007FF773E2197C 48 8B 45 08          mov         rax,qword ptr [rbp+8]
00007FF773E21980 48 89 45 28          mov         qword ptr [rbp+28h],rax
	13: 	Person* p3 = (Person*)p1;
00007FF773E21984 48 8B 45 08          mov         rax,qword ptr [rbp+8]
00007FF773E21988 48 89 45 48          mov         qword ptr [rbp+48h],rax
*/
dynamic_cast

一般用于多态类型的转换,有运行安全检测

#include <iostream>
using namespace std;

class Person
{
	virtual void run()
	{

	}
};
class Student : public Person
{

};
class Car
{

};
int main(void)
{
	Person* p1 = new Person();
	Person* p2 = new Student();
	cout << "P1 = " << p1 << endl;
	cout << "P2 = " << p2 << endl;
	//用于多态类型转换student和Person没有多态没有虚函数会报错
	//不太安全,Person赋值给student,子类赋值给父类,不安全
	Student* stu1 = dynamic_cast<Student *>(p1); //不安全
	Student* stu2 = dynamic_cast<Student *>(p2); //安全
	//其它方式会把地址完全转过来
	cout << "stu1 = " << stu1 << endl; //不安全会清空,安全检测
	cout << "stu2 = " << stu2 << endl;
	return 0;
}
/*
P1 = 0000019F7CD6C8B0
P2 = 0000019F7CD6C2C0 
stu1 = 0000000000000000 //p1赋值失败
stu2 = 0000019F7CD6C2C0 //p2成功

*/
static_cast
  • 对比dynamic_cast,缺乏运行时安全检测
  • 不能交叉转换(不是同一继承体系的,无法转换)
  • 常用语基本数据类型的转换,非const转成const
  • 适用较广
Person* p1 = new Person();
	Car* c1 = dynamic_cast<Car*>(p1);
	//报错,交叉转换不允许car和Person没有任何关系
	//Car* c2 = static_cast<Car*>(p1);
	//基本类型转换
	int a = 10;
	double d = static_cast<double>(a);
	return 0;
reinterpret_cast
  • 属于比较底层的强制转换,没有任何类型检查和格式转换,仅仅是简单的二进制数据拷贝
  • 可以交叉转换
  • 可以将指针和整数相互转换
#include <iostream>
using namespace std;
int main(void)
{
	int a = 10;
	//小端模式从后面开始读
	//二进制 0000 1010 0000 0000 0000 0000
	//十六进制: 0A 00 00 00
	//int a = 10;
	//double八个字节,double和int并不能简单的二进制copy
	// 00 00 00 00 00 00 24 40
	double d = reinterpret_cast<double&>(a);
	//二进制纯copy
	cout << "a= " << a << endl;
	cout << "d= " << d << endl;
	return 0;
}
/*
a= 10
d= 8.33713e+80
*/

参考资料:《30小时快速精通C++和外挂》
不是同一继承体系的,无法转换)

  • 常用语基本数据类型的转换,非const转成const
  • 适用较广
Person* p1 = new Person();
	Car* c1 = dynamic_cast<Car*>(p1);
	//报错,交叉转换不允许car和Person没有任何关系
	//Car* c2 = static_cast<Car*>(p1);
	//基本类型转换
	int a = 10;
	double d = static_cast<double>(a);
	return 0;
reinterpret_cast
  • 属于比较底层的强制转换,没有任何类型检查和格式转换,仅仅是简单的二进制数据拷贝
  • 可以交叉转换
  • 可以将指针和整数相互转换
#include <iostream>
using namespace std;
int main(void)
{
	int a = 10;
	//小端模式从后面开始读
	//二进制 0000 1010 0000 0000 0000 0000
	//十六进制: 0A 00 00 00
	//int a = 10;
	//double八个字节,double和int并不能简单的二进制copy
	// 00 00 00 00 00 00 24 40
	double d = reinterpret_cast<double&>(a);
	//二进制纯copy
	cout << "a= " << a << endl;
	cout << "d= " << d << endl;
	return 0;
}
/*
a= 10
d= 8.33713e+80
*/

参考资料:《30小时快速精通C++和外挂》


  1. https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值