前言
这个节主要讲解如何使用动态的调用相应的函数,如果你对此有充分的理解可直接跳过此节,这个是为后面的课程打下基础,因为后面的章节几乎每一篇幅都涉及到指针(后续所有代码均采用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;
}
三、运行测试
如下图所示:
上述的代码已经非常明确了它的概念,总结起来就是函数指针本身是一个变量,也是一个指针,有它自己的内存地址(变量所在的地址),也有内存地址所保存的数值(变量对应的数值即函数地址,也是函数对应的机器码字节)。