滴水-day18-数组指针-函数指针
数组指针
// 二维数组指针
char(*arr)[2][3] = (char(*)[2][3])array;
printf("%x\\\\n", *(*(*(arr + 2) + 3) + 4));
//3*6+2*3+5==18+6+5=29
printf("%x\\\\n", *(*(*(arr + 3) + 2) + 5));
//printf("%x\\\\n", array[29]);
// 二维数组指针
int(*arr)[2][3] = (int(*)[2][3])array;
// 4*2*3*2 + 4*3*2 + 1*2 == 48 + 24 + 8 == 80
printf("%x\\\\n", *(*(*(arr + 2) + 2) + 2));
printf("%x\\\\n", array[80]);
//三维数组指针
int(*arr)[2][3][2] = (int(*)[2][3][2])array;
printf("%p\\\\n", *(*(*(arr + 1) + 1) + 1) + 1);
//00000f00
printf("三:%x\\\\n", *(*(*(*(arr + 1) + 1) + 1) + 1));
//00000f00
printf("%x\\\\n", array[84]);
这一块要多加练习,不然很容易忘记。遇到多维数组指针就一层一层的去计算,直到计算到最后一层。
加法运算时要注意数据类型的宽度。学习指针就是在学习数据类型的宽度,宽度这一块一定要熟悉。
函数指针
函数指针
函数指针与指针函数不同,指针函数代表了这个函数返回的是一个指针类型
在C语言中,函数名也是全局变量,保存的是函数所执行的代码,这些可称为硬编码。
而函数指针表示这个指针指向一个函数的首地址。
这样就可以看到全局变量和函数名都属于全局变量。
// 此时p指向了addSum的首地址
int(*p)(int, int) = &addSum;
int result = p(2, 3);
printf("%d\\\\n", result);
printf("%p\\\\n", &addSum); // 00961226
printf("%p\\\\n", p); // 00961226
printf("%d\\\\n", x);
// 如果指向的函数返回类型和参数类型与这个指针一致那么(int(*)(int,int))可以省略
int(*p)(int, int) = (int(*)(int,int)) & addSum;
通过p指向一个函数,再通过p调用函数传递参数,这个通常在加载DLL的时候需要使用。
函数指针无法进行++--等运算,因为函数的宽度是不固定的,有的函数长有的函数短。
int(*xx)(int) = (int(__cdecl *)(int))10;
这种情况下需要强制类型转换,虽然10不知指向了哪里,但一定不是可访问的地址。
函数是一个全局变量,里面保存了这个函数所要执行的代码,在内存中是以硬编码的形式保存的
既然这样可以通过硬编码的方式去完成一个函数,使用数组保存这些硬编码,在使用函数指针去指向这个数组进行函数调用。
addSum函数的硬编码:
00401010 55 push ebp
00401011 8B EC mov ebp,esp
00401013 83 EC 40 sub esp,40h
00401016 53 push ebx
00401017 56 push esi
00401018 57 push edi
00401019 8D 7D C0 lea edi,[ebp-40h]
0040101C B9 10 00 00 00 mov ecx,10h
00401021 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401026 F3 AB rep stos dword ptr [edi]
00401028 8B 45 08 mov eax,dword ptr [ebp+8]
0040102B 03 45 0C add eax,dword ptr [ebp+0Ch]
0040102E 5F pop edi
0040102F 5E pop esi
00401030 5B pop ebx
00401031 8B E5 mov esp,ebp
00401033 5D pop ebp
00401034 C3 ret
从左到右是地址,硬编码,汇编指令
硬编码对应这一条汇编指向,是不会变的,除非自己设计硬编码。
比如0x55,永远表示为:push ebp
提取出这些硬编码
char fun[] = {
0x55,
0x8B, 0xEC,
0x81, 0xEC, 0xC0, 0x00, 0x00, 0x00,
0x53,
0x56,
0x57,
0x8B, 0xFD,
0x33, 0xC9,
0xB8, 0xCC, 0xCC, 0xCC, 0xCC,
0xF3, 0xAB,
0xB9, 0x29, 0xC0, 0xEA, 0x00,
0xE8, 0xDF, 0xFA, 0xFF, 0xFF,
0x8B, 0x45, 0x08,
0x03, 0x45, 0x0C,
0xEB, 0x02,
0x33, 0xC0,
0x5F,
0x5E,
0x5B,
0x81, 0xC4, 0xC0, 0x00, 0x00, 0x00,
0x3B, 0xEC,
0xE8, 0xEE, 0xF9, 0xFF, 0xFF,
0x8B, 0xE5,
0x5D,
0xC3,
};
看似是存储了一些数据,其实这些数据就是addSum函数所要执行的代码。通过函数指针可以调用这个数组
// 需要强制类型转换,因为是个char类型,需要转为int(*)(int,int)
int(*funP)(int, int) = (int(*)(int,int))&fun;
代码已转为汇编指令,但是在vs2019中无法执行,会发生异常,内存访问错误。
在WINXP中VC++6.0可以运行。
练习
License:
CC BY 4.0