C++逆向----基础语法(一)
- C++的源文件扩展名是:cpp(c plus plus的简称)。
- C++程序的入口是main函数。
- C++完全兼容C语言的语法。
cin/cout语法
- C++中常使用cin控制输入,使用cout控制输出。
cout语法
代码
cout << "Hello"; //<<运算符重载
cout << endl; //相当于换行,此处不加双引号的
cout << "World";
运行结果
另一种实现方法
cout << "Hello" << endl << "World";
运行结果
cin语法
代码
int age;
cin >> age; //把输入的值赋值给age变量
cout << "age is " << age << endl;
运行结果
补充知识
getchar()函数是等待键盘输入(如果敲回车,就会读取键盘输入,这样就不会退出main函数,只有输入东西才能退出,这样的话我们才能去使用F5执行程序时,才可以有停留,不然会直接一闪而过。)
函数重载(Overload)
什么是函数重载?
- 函数名一样
- 参数的顺序,参数的类型,参数的内容,个数不同。
- 返回值类型与函数重载无关,不能构成重载。
- C语言不支持函数重载
- 隐式转换存在二异性。
代码
#include <iostream>
using namespace std;
int sum(int v1,int v2) {
return v1 + v2;
}
int sum(int v1,int v2,int v3) {
return v1 + v2 + v3;
}
void func(int v1, double v2) {
cout << "func(int v1, double v2)" << endl;
}
void func(double v1, int v2) {
cout << "func(double v1, int v2)" << endl;
}
int main() {
/*
C语言是不支持函数的重载的
C++支持函数重载
*/
cout << sum(10,20) << endl;
cout << sum(10, 20, 30) << endl;
func(10, 1.5);
func(1.5, 10);
getchar();
return 0;
}
运行结果
为什么C++支持函数重载?
- C++采用了name mangling或者叫name decoration技术
- 重载的时候生成多个不同函数名的函数,比如deploy(int a)和deploy(double a)这两个函数是重载的,可能分别被编译器编译为deploy_int(int a)和deploy_double(double a)
- 但是不同的编译器对C++有不同的重载方式。
从汇编的角度去理解函数的重载
- j进入调试状态,插入断点,这样才能出现反汇编的按钮
- F5运行,然后右键点击反汇编按钮
- 比较函数func(int v1,double v2)和函数func(double v1,int v2)的不同,显然函数转成的内存地址是不同的,所以函数也是不同的。
补充知识
内存地址和机器码
00F428B4 6A 0A
00F428B6 83 EC 08
- 两者内存地址是连续的,00F428B4后跟了两个字节,所以后面的是00F428B6。
进制和字节的关系
- 8个二进制代表2个16进制。
- 两个16进制代表2个字节。
- 举例:6A 0A 这个机器码占了2个字节
从IDA的角度去分析函数重载
- 将C++运行生成的exe文件拖入IDA,这个是在Debug模式下生成的,含有很多的调试信息。
- 将模式调成Release模式,生成exe,拖入IDA,但是release模式下做了优化
- 在release模式下取消优化
- 点击F5运行,重新生成exe文件,拖入IDA,发现函数名是不同的。
- 在release模式下取消优化
补充知识
- Debug 模式:很多的调试信息
- Release 模式:去除了很多的调试信息,生成的可执行文件比较简洁,高效。编译器会对函数进行优化。
默认参数
C++允许函数设置默认参数,在调用时可以根据情况省略实参
- 默认参数只能按照从右到左的顺序。
- 如果函数同时有声明、实现,默认参数只能放在函数声明中。
- 默认参数的值可以是变量、全局符号(全局变量、函数名)。
案例代码
#include <iostream>
using namespace std;
int sum(int v1, int v2) {
return v1 + v2;
}
int main() {
cout << sum(10, 20) << endl;
getchar();
return 0;
}
默认参数修改
#include <iostream>
using namespace std;
int sum(int v1=5, int v2=6) {
return v1 + v2;
}
int main() {
cout << sum() << endl;
cout << sum(10) << endl;
cout << sum(10, 20) << endl;
getchar();
return 0;
}
- cout << sum() << endl; 代表使用默认参数,v1=5,v2=6
- cout << sum(10) << endl; 代表v1=10,v2使用默认参数6
- cout << sum(10, 20) << endl; 代表v1=10, v2=20
参数传递规则
- 参数只能传递从左往右。
- 必须保证最右面的参数有默认值,或者存在传递的实参。
- 但是默认参数必须要从最右边开始。
默认函数产生二义性
- 函数重载、默认参数可能产生冲突,二义性(优先选择使用默认参数)
从汇编的角度去理解默认参数的原理
程序代码1:
#include <iostream>
using namespace std;
int sum(int v1, int v2) {
return v1 + v2;
}
int main() {
sum(1, 4);
sum(2, 5);
getchar();
return 0;
}
汇编:两个函数的地址一样
程序代码2(含有默认参数):
#include <iostream>
using namespace std;
int sum(int v1, int v2=5) {
return v1 + v2;
}
int main() {
sum(1);
sum(1, 5);
getchar();
return 0;
}
汇编:两种调用时汇编是一样的
extern “C”
- 被extern "C"修饰的代码会被按照C语言的方式去进行编译。
- 但是C语言是不支持函数重载的。
extern “C” 案例
程序代码:
我们使用extern "C"去执行函数重载的函数
#include <iostream>
using namespace std;
extern "C" void func(){
}
extern "C" void func(int v1) {
}
int main() {
getchar();
return 0;
}
运行结果:
如图所示函数按照C语言的方式编译,是不支持重载的。
extern “C” 用途
- 由于C和C++的编译规则不同,多用于C和C++混合开发。
- 在开发时使用第三方框架\库,这些可能是用C语言写的。
C语言库
int sum(int v1, int v2)
{
return v1 + v2;
}
int delta(int v1, int v2)
{
return v1 - v2;
}
C++代码实现1:
#include <iostream>
using namespace std;
extern "C" { //使用extern "C"是告诉编译器使用C语言的编译方式进行编译的
int sum(int v1, int v2);
int delta(int v1, int v2);
}
int main() {
cout << sum(20, 10) << endl;
cout << delta(20, 10) << endl;
getchar();
return 0;
}
C++代码实现2:
- .cpp文件为C++文件
- .c文件为第三方C语言库
- .h为头文件,存放函数声明,这样可以使第三方库更便捷的去调用。
#include <iostream>
using namespace std;
#include"math.h"
int main() {
cout << sum(20, 10) << endl;
cout << delta(20, 10) << endl;
getchar();
return 0;
}
头文件实现:
// header file:存放函数的声明
extern "C"{
int sum(int v1, int v2);
int delta(int v1, int v2);
}
宏定义 #define _cplusplus
- C++默认存在宏定义 #define _cplusplus
- 通过对宏定义的判断,我们可以让c语言代码去调用带有extern “C”的头文件。
此时的头文件代码:
// header file:存放函数的声明
#ifdef _cplusplus
extern "C"
#endif // _cplusplus
int sum(int v1, int v2);
int delta(int v1, int v2);
#ifdef _cplusplus
}
#endif // _cplusplus
宏定义 #define _cplusplus
- C++默认存在宏定义 #define _cplusplus
- 通过对宏定义的判断,我们可以让c语言代码去调用带有extern “C”的头文件。
此时的头文件代码:
// header file:存放函数的声明
#ifdef _cplusplus
extern "C"
#endif // _cplusplus
int sum(int v1, int v2);
int delta(int v1, int v2);
#ifdef _cplusplus
}
#endif // _cplusplus