一、C++反作弊对抗实战 (基础篇 —— 1.什么是指针与函数指针)

270 篇文章 9 订阅

前言

  这个节主要讲解如何使用动态的调用相应的函数,如果你对此有充分的理解可直接跳过此节,这个是为后面的课程打下基础,因为后面的章节几乎每一篇幅都涉及到指针(后续所有代码均采用VS2010 + Win7 x64环境编写测试)。

  我们在这里先回顾一个指针的概念,网上目前有太多的文章在讲解指针的概念。作为一个当年的C语言的初学者,书本上的内容介绍着实让我头疼许久,始终都觉得没有那么详细。

  这就跟前几年回老家教父母用电脑在U盘拷贝粘贴图片一样,在他们老人家看来复制、剪切、粘贴这三个词非常难理解,后来我想到了一个生活中的妙招便拿来给他们举例翻译:

  复制:把刷子从油漆桶里蘸一下

  剪切:把一桶油漆全部倒刷子上面了

  粘贴:向墙壁上刷漆。如果之前是复制的话还能一直无限的粘贴,如果之前是剪切的话,那就只能刷一次就不能再刷了

  至此他们二老就彻底明白了这三个词的含义!

  所以,在这里我们举个日常生活中常见的例子,这应该能让你轻易明白指针到底是个什么玩意儿:

  上面这张图就很形象的说明了指针的应用:

  1. 首先把整个柜子看作是一片内存空间,每个格子就是就个32/64位的单元格(默认指针占用的大小sizeof是32或64位);

  2. 指针本身跟int, char 等数据类型一样,它只是一种特殊的数据类型,它也有它自己的内存地址,也有它的存储空间(存储内容,此处代表一张纸);

  3. 这个开着门的单元格中的数值(这张纸)却是另一个单元格在柜子(内存)中的一个坐标位置(内存地址)。

  如果我们用代码来形容他的话,如下图所示:

  所以,在本专栏后续的文章中,我将尽采用类似通俗易懂的例子解释每个章节核心概念;在此,也希望与大家一起共同探讨、学习交流!


一、什么是函数指针?

  函数指针是一个指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数地址,这个指针对应的值(强转成char *),便是这个函数地址所对应固定的机器码字节,而指针变量的地址并不是固定的。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。

  函数指针有两个用途:调用函数和做函数的参数。 通俗的讲,函数指针的使用,通常为了应对特殊环境下的调用,这里“特殊”一处在后续将会逐步理解。

二、函数指针示例

1.常规函数调用

  在没有使用函数指针前,我们通常是直接包含头文件、引入LIB库来直接调用,代码如下(示例):

#include "stdafx.h"
 
#include <windows.h>
#pragma comment(lib, "user32.lib")
 
int _tmain(int argc, _TCHAR* argv[])
{
	MessageBox(NULL, L"Hello World !", L"Title", MB_OK);
	return 0;
}
 

2.函数指针示例a

  我们可以用下面函数指针的方法调用,这里是首先直接将lib库中的MessageBox函数地址赋值给函数指针变量pMsgBox,然后直接调用函数指针也可以达到同样效果,代码如下(示例):

#include "stdafx.h"
 
#include <windows.h>
#pragma comment(lib, "user32.lib")
 
typedef int (WINAPI *fMessageBox)( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
 
fMessageBox		pMsgBox = NULL;
 
int _tmain(int argc, _TCHAR* argv[])
{
	pMsgBox = MessageBox;
	pMsgBox(NULL, L"Hello World !", L"Title", MB_OK);
	return 0;
}

3.函数指针示例b

  上述两种只是为了能逐渐慢慢理解函数指针的概念,我们也可以不用引入lib库的方法来调用,这种方式也将是后续代码中经常使用到的函数调用方法,代码如下(示例):

#include "stdafx.h"
 
#include <windows.h>
 
typedef int (WINAPI *fMessageBox)( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
 
fMessageBox		pMsgBox = NULL;
HMODULE			g_hUser32 = NULL;
 
int _tmain(int argc, _TCHAR* argv[])
{
	// 动态加载dll
	g_hUser32 = LoadLibrary(L"user32.dll");
	if(NULL == g_hUser32)
		return 0;
 
	// 获取函数指针
	pMsgBox = (fMessageBox)GetProcAddress(g_hUser32, "MessageBoxW");
	if(pMsgBox)
		pMsgBox(NULL, L"Hello World !", L"Title", MB_OK);
 
	// 释放dll
	FreeLibrary(g_hUser32);
	return 0;
}


三、运行测试

  如下图所示:

  上述的代码已经非常明确了它的概念,总结起来就是函数指针本身是一个变量,也是一个指针,有它自己的内存地址(变量所在的地址),也有内存地址所保存的数值(变量对应的数值即函数地址,也是函数对应的机器码字节)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪宁宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值