一、typedef基本介绍
typedef作用是为变量引入一种新的名字,typedef的格式与变量申明完全一致,只是在前面加了一个typedef关键字。typedef并没有创建一个新的变量,只是表明这个名字是指定类型的同义词。简单的例子如下:
typedef int Length;
Length len,maxlen;
但是,一般情况,采用typedef就是为了让复杂的申明方式变得简单,比如结构体(Struct)、指针申明的变量:
typedef char *String;
typedef中声明的类型在变量名的位置出现,而不是紧接在关键字typedef之后,上面的代码表示的类型是char *,类型名是String。
char *lineptr[MAXLINES];
char *alloc(int n);
/* 上面的代码等价于 */
String lineptr[MAXLINES],alloc(int);
上面的代码固然是有点用,但还算简单。
typedef类似于#define语句,但由于typedef是由编译器解释的(#define通过预处理器实现替换),因此它的文本替换功能要超过预处理器(#define)的能力。比如typedef可以作用于指向函数的指针,如下:
typedef int (*FP)();//pointer to function returning int
typedef int F(int);//function with one int parameter,returning int
该怎么使用呢?
FP fp;//pointer to a function returning int
F *fp2;//pointer to a function taking an int parameter and returning an int
采用typedef重命名指向函数的指针,能有多大用呢?有些地方还真是很有用。
二、typedef的高级应用
来看一个高级一点的例子,那就是DLL(动态链接库)共享内存问题。多个进程可以共享动态链接库中相同的代码,但是由DLL维护的数据对每个进程是不一样的,所以每个进程都有自己的、供DLL使用数据的地址空间,要实现进程间共享数据还需要一些额外的工作。
这里还是拿《windows程序设计》中的代码例子来说明。先看关于一个关于DLL链接库模块的代码:
/*----------------------
STRLIB.H header file
----------------------*/
#ifdef __cplusplus
#define EXPORT extern "C" __declspec (dllexport)
#else
#define EXPORT __declspec (dllexport)
#endif
// The maximum number of strings STRLIB will store and their lengths
#define MAX_STRINGS 256
#define MAX_LENGTH 64
// The callback function type definition uses generic strings
typedef BOOL (CALLBACK * GETSTRCB) (PCTSTR, PVOID) ;
// Each function has ANSI and Unicode versions
EXPORT BOOL CALLBACK AddStringA (PCSTR) ;
EXPORT BOOL CALLBACK AddStringW (PCWSTR) ;
EXPORT BOOL CALLBACK DeleteStringA (PCSTR) ;
EXPORT BOOL CALLBACK DeleteStringW (PCWSTR) ;
EXPORT int CALLBACK GetStringsA (GETSTRCB, PVOID) ;
EXPORT int CALLBACK GetStringsW (GETSTRCB, PVOID) ;
// Use the correct version depending on the UNICODE identifier
#ifdef UNICODE
#define AddString AddStringW
#define DeleteString DeleteStringW
#define GetStrings GetStringsW
#else
#define AddString AddStringA
#define DeleteString DeleteStringA
#define GetStrings GetStringsA
#endif
/*------------------------------------------------
STRLIB.C -- Library module for STRPROG program
(c) Charles Petzold, 1998
------------------------------------------------*/
#include <windows.h>
#include <wchar.h> // for wide-character string functions
#include "strlib.h"
// shared memory section (requires /SECTION:shared,RWS in link options)
#pragma data_seg ("shared")
int iTotal = 0 ;
WCHAR szStrings [MAX_STRINGS][MAX_LENGTH + 1] = { '\0' } ;
#pragma data_seg ()
#pragma comment(linker,"/SECTION:shared,RWS")
int WINAPI DllMain (HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
return TRUE ;
}
EXPORT BOOL CALLBACK AddStringA (PCSTR pStringIn)
{
BOOL bReturn ;
int iLength ;
PWSTR pWideStr ;
// Convert string to Unicode and call AddStringW
iLength = MultiByteToWideChar (CP_ACP, 0, pStringIn, -1, NULL, 0) ;
pWideStr = malloc (iLength) ;
MultiByteToWideChar (CP_ACP, 0, pStringIn, -1, pWideStr, iLength) ;
bReturn = AddStringW (pWideStr) ;
free (pWideStr) ;
return bReturn ;
}
EXPORT BOOL CALLBACK AddStringW (PCWSTR pStringIn)
{
PWSTR pString ;
int i, iLength ;
if (iTotal == MAX_STRINGS - 1)
return FALSE ;
if ((iLength = wcslen (pStringIn)) == 0)
return FALSE ;
// Allocate memory for storing string, copy it, convert to upper case
pString = malloc (sizeof (WCHAR) * (1 + iLength)) ;
wcscpy (pString, pStringIn) ;
_wcsupr (pString) ;
// Alphabetize the strings
for (i = iTotal ; i > 0 ; i--)
{
if (wcscmp (pString, szStrings[i - 1]) >= 0)
break ;
wcscpy (szStrings[i], szStrings[i - 1]) ;
}
wcscpy (szStrings[i], pString) ;
iTotal++ ;
free (pString) ;
return TRUE ;
}
EXPORT BOOL CALLBACK DeleteStringA (PCSTR pStringIn)
{
BOOL bReturn ;
int iLength ;
PWSTR pWideStr ;
// Convert string to Unicode and call DeleteStringW
iLength = MultiByteToWideChar (CP_ACP, 0, pStringIn, -1, NULL, 0) ;
pWideStr = malloc (iLength) ;
MultiByteToWideChar (CP_ACP, 0, pStringIn, -1, pWideStr, iLength) ;
bReturn = DeleteStringW (pWideStr) ;
free (pWideStr) ;
return bReturn ;
}
EXPORT BOOL CALLBACK DeleteStringW (PCWSTR pStringIn)
{
int i, j ;
if (0 == wcslen (pStringIn))
return FALSE ;
for (i = 0 ; i < iTotal ; i++)
{
if (_wcsicmp (szStrings[i], pStringIn) == 0)
break ;
}
// If given string not in list, return without taking action
if (i == iTotal)
return FALSE ;
// Else adjust list downward
for (j = i ; j < iTotal ; j++)
wcscpy (szStrings[j], szStrings[j + 1]) ;
szStrings[iTotal--][0] = '\0' ;
return TRUE ;
}
EXPORT int CALLBACK GetStringsA (GETSTRCB pfnGetStrCallBack, PVOID pParam)
{
BOOL bReturn ;
int i, iLength ;
PSTR pAnsiStr ;
for (i = 0 ; i < iTotal ; i++)
{
// Convert string from Unicode
iLength = WideCharToMultiByte (CP_ACP, 0, szStrings[i], -1, NULL, 0,
NULL, NULL) ;
pAnsiStr = malloc (iLength) ;
WideCharToMultiByte (CP_ACP, 0, szStrings[i], -1, pAnsiStr, iLength,
NULL, NULL) ;
// Call callback function
bReturn = pfnGetStrCallBack (pAnsiStr, pParam) ;
if (bReturn == FALSE)
return i + 1 ;
free (pAnsiStr) ;
}
return iTotal ;
}
EXPORT int CALLBACK GetStringsW (GETSTRCB pfnGetStrCallBack, PVOID pParam)
{
BOOL bReturn ;
int i ;
for (i = 0 ; i < iTotal ; i++)
{
bReturn = pfnGetStrCallBack ((PCTSTR)szStrings[i], pParam) ;
if (bReturn == FALSE)
return i + 1 ;
}
return iTotal ;
}
上面的代码,就是创建一个dll文件,实现对UNICODE与非UNICODE版本的字串符增加、删除、获取的函数。但是还是有几点需要强调一下。
1,使C和C++都能使用该DLL。需要在strlib.h文件头部加上以下代码:
#ifdef __cplusplus
#define EXPORT extern "C" __declspec (dllexport)
#else
#define EXPORT __declspec (dllexport)
#endif
2,实现DLL共享内存需要在strlib.c文件中做特别申明:
// shared memory section (requires /SECTION:shared,RWS in link options)
#pragma data_seg ("shared")
int iTotal = 0 ;
WCHAR szStrings [MAX_STRINGS][MAX_LENGTH + 1] = { '\0' } ;
#pragma data_seg ()
#pragma comment(linker,"/SECTION:shared,RWS")
上面的"shared"表示该数据区的名字,当然也可以定义成其它的名字。iTotal、szStrings表示需要共享的变量。最后一句,表示将shared数据区,通知给链接器,RWS表明该数据区具有可读、可写和可共享的属性。
3,typedef与回调函数的密切配合,在strlib.h文件中有句代码:
typedef BOOL (CALLBACK * GETSTRCB) (PCTSTR, PVOID) ;
这个代码的用处在strlib.c文件中体现出了价值:
EXPORT int CALLBACK GetStringsA (GETSTRCB pfnGetStrCallBack, PVOID pParam){
....
bReturn = pfnGetStrCallBack (pAnsiStr, pParam) ;
....
}
EXPORT int CALLBACK GetStringsW (GETSTRCB pfnGetStrCallBack, PVOID pParam)
{
....
bReturn = pfnGetStrCallBack (szStrings[i], pParam) ;
....
}
可以看出,上面的pfnGetStrCallBack参数就是一个函数,但是函数的声明来自于strlib.h,但是具体的实现却在外部的调用程序中。这种通过typedef在头文件声明的方式让程序的结构性和可读性都有较大提升。
下面是调用这个dll的程序代码(rc和头文件省略):
/*--------------------------------------------------------
STRPROG.C -- Program using STRLIB dynamic-link library
(c) Charles Petzold, 1998
--------------------------------------------------------*/
#include <windows.h>
#include "strlib.h"
#include "resource.h"
typedef struct
{
HDC hdc ;
int xText ;
int yText ;
int xStart ;
int yStart ;
int xIncr ;
int yIncr ;
int xMax ;
int yMax ;
}
CBPARAM ;
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szAppName [] = TEXT ("StrProg") ;
TCHAR szString [MAX_LENGTH + 1] ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("DLL Demonstration Program"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
SendDlgItemMessage (hDlg, IDC_STRING, EM_LIMITTEXT, MAX_LENGTH, 0) ;
return TRUE ;
case WM_COMMAND:
switch (wParam)
{
case IDOK:
GetDlgItemText (hDlg, IDC_STRING, szString, MAX_LENGTH) ;
EndDialog (hDlg, TRUE) ;
return TRUE ;
case IDCANCEL:
EndDialog (hDlg, FALSE) ;
return TRUE ;
}
}
return FALSE ;
}
BOOL CALLBACK GetStrCallBack (PTSTR pString, CBPARAM * pcbp)
{
TextOut (pcbp->hdc, pcbp->xText, pcbp->yText,
pString, lstrlen (pString)) ;
if ((pcbp->yText += pcbp->yIncr) > pcbp->yMax)
{
pcbp->yText = pcbp->yStart ;
if ((pcbp->xText += pcbp->xIncr) > pcbp->xMax)
return FALSE ;
}
return TRUE ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HINSTANCE hInst ;
static int cxChar, cyChar, cxClient, cyClient ;
static UINT iDataChangeMsg ;
CBPARAM cbparam ;
HDC hdc ;
PAINTSTRUCT ps ;
TEXTMETRIC tm ;
switch (message)
{
case WM_CREATE:
hInst = ((LPCREATESTRUCT) lParam)->hInstance ;
hdc = GetDC (hwnd) ;
GetTextMetrics (hdc, &tm) ;
cxChar = (int) tm.tmAveCharWidth ;
cyChar = (int) (tm.tmHeight + tm.tmExternalLeading) ;
ReleaseDC (hwnd, hdc) ;
// Register message for notifying instances of data changes
iDataChangeMsg = RegisterWindowMessage (TEXT ("StrProgDataChange")) ;
return 0 ;
case WM_COMMAND:
switch (wParam)
{
case IDM_ENTER:
if (DialogBox (hInst, TEXT ("EnterDlg"), hwnd, &DlgProc))
{
if (AddString (szString))
PostMessage (HWND_BROADCAST, iDataChangeMsg, 0, 0) ;
else
MessageBeep (0) ;
}
break ;
case IDM_DELETE:
if (DialogBox (hInst, TEXT ("DeleteDlg"), hwnd, &DlgProc))
{
if (DeleteString (szString))
PostMessage (HWND_BROADCAST, iDataChangeMsg, 0, 0) ;
else
MessageBeep (0) ;
}
break ;
}
return 0 ;
case WM_SIZE:
cxClient = (int) LOWORD (lParam) ;
cyClient = (int) HIWORD (lParam) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
cbparam.hdc = hdc ;
cbparam.xText = cbparam.xStart = cxChar ;
cbparam.yText = cbparam.yStart = cyChar ;
cbparam.xIncr = cxChar * MAX_LENGTH ;
cbparam.yIncr = cyChar ;
cbparam.xMax = cbparam.xIncr * (1 + cxClient / cbparam.xIncr) ;
cbparam.yMax = cyChar * (cyClient / cyChar - 1) ;
GetStrings ((GETSTRCB) GetStrCallBack, (PVOID) &cbparam) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
default:
if (message == iDataChangeMsg)
InvalidateRect (hwnd, NULL, TRUE) ;
break ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
这个程序的起始处便通过#include "strlib.h"引用dll的头文件,得到GETSTRCB函数指针的定义格式,然后编写具体实现该函数的细节(即dll文件中,只是定义了一个函数的名字,具体实现在外部调用的程序中实现):
BOOL CALLBACK GetStrCallBack (PTSTR pString, CBPARAM * pcbp)
{
TextOut (pcbp->hdc, pcbp->xText, pcbp->yText,
pString, lstrlen (pString)) ;
if ((pcbp->yText += pcbp->yIncr) > pcbp->yMax)
{
pcbp->yText = pcbp->yStart ;
if ((pcbp->xText += pcbp->xIncr) > pcbp->xMax)
return FALSE ;
}
return TRUE ;
}
....
//调用时再转型
GetStrings ((GETSTRCB) GetStrCallBack, (PVOID) &cbparam) ;
注:1,这个程序有点复杂,但正如所看到的那样,只有在复杂的程序中,才更能体现出typedef的价值;
2,这个程序在VC++中可以实现内存共享,但是如果用dev c++,则共享没有效果。
该程序实现效果如下:
也就是同时打开多个对话框时,在任意一个对话框中输入一些数据,那么另一个对话框内的数据也会跟着发生变化。