指针(七)
- 数组指针
- 函数指针
一、多维数组指针
1)用一维数组指针访问一维数组
代码:
1 void fun()
2 {
3 int arr[5] = {1,2,3,4,5};
4
5 int (*p)[5] = &arr;
6 printf("%d\n", *(*(p) + 2)); //结果是3
7
8 int (*px)[2] = (int (*)[2])arr; //强转为宽度为两个int的数组指针类型
9 printf("%d\n", *(*(px + 1) + 1)); //结果是4
10 }
分析:
取地址运算&后的arr是其类型加上一个*,也就是int (*)[5];
&arr的值和arr的值一样都是数组的首地址,只是类型不一样;
*p是数组类型,相当于int*,根据带*类型的加法特性,相当于加2个int宽度,也就是从执行1到指向了3;
int*做解引用,结果是3;
px是两个int的数组指针,用强转的方式给它赋值,此时指向arr首地址;
px+1的结果是加上px的类型去掉一个*的宽度,也就是加上两个int的宽度;
*(px+1)是数组类型,相当于int*,加1后再加上一个int宽度;
总共加了3个int宽度,指向了4;
2)一维数组指针访问二维数组
代码:
1 void fun()
2 {
3 int arr[2][5] = {{1,2,3,4,5},{6,7,8,9,10}};
4
5 int (*p)[5] = (int (*)[5])arr;
6 printf("%d\n", *(*(p) + 2)); //结果是3
7 }
分析:
多维数组转成汇编本质上和一维数组一样;
3)二维数组指针访问一维数组
代码:
1 void fun()
2 {
3 int arr[15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
4
5 int (*p)[2][3] =(int (*)[2][3]) arr;
6 printf("%d\n", *(*(*(p)+1) + 2)); //6
7 printf("%d\n", *(*(*(p+1)+1) + 2)); //12
8 printf("%d\n", p[1][1][2]); //12
9 printf("%d\n", (*(*(p)+1))[2]); //6
10 }
4)二维数组指针访问二维数组
代码:
1 void fun()
2 {
3 int arr[2][7] = {{1,2,3,4,5,6,7},{8,9,10,11,12,13,14}};
4
5 int (*p)[2][3] =(int (*)[2][3]) arr;
6 printf("%d\n", *(*(*(p)+1) + 2)); //6
7 printf("%d\n", *(*(*(p+1)+1) + 2)); //12
8 printf("%d\n", p[1][1][2]); //12
9 printf("%d\n", (*(*(p)+1))[2]); //6
10
11 int (*px)[2][7] = &arr;
12 printf("%d\n", (*px)[1][2]); //10
13 }
二、函数指针
代码和数据本质上都是一堆二进制数;
因此,在编译器看来,函数和和变量没有本质的区别;
编译器将函数名当做全局变量;
只要是变量都可以加*,变成指针类型;
1)函数指针的声明:
返回类型(*变量名)(参数表)
如:
1 int (*pFun)(int,int);
2)赋值
1 pFun = (int (*)(int,int))10;
或者
1 pFun = 函数名.
可以给函数指针赋任何值,但为了能正确运行,需要将返回值和参数类型正确的函数的地址赋值给函数指针;
将函数名赋值给函数指针时,编译器会检查函数的参数和返回值是否正确,如果正确则通过编译,所以没必要强制转型了;
3)使用
和函数使用方式相同
1 int plus(int x, int y)
2 {
3 return x+y;
4 }
5
6 void fun()
7 {
8 int (*p)(int,int) = plus;
9 printf("%d",p(1,2)); //3
10 }
4)特性
宽度:
任何指针类型宽度为4,函数指针也一样;
运算:++、--、+整数、-整数、相减
因为函数的代码宽度不能确定,所以无法确定函数指针去掉一个*后的宽度;
因此函数指针无法做运算;
比较:
函数指针可以做比较;
5)别名表示
1 typedef int (*Fun)(int,int);
这个不是变量的声明,而是为函数指针起个别名:Fun 相当于函数指针类型
1 Fun p;
这个才是变量的声明,p是变量,Fun是类型.
6)应用
利用函数指针将代码隐藏到数据区;
可以达到保护函数的目的:
将函数编译后生成的二进制文件抠出来,放在数据区,例如数组;
然后在数组上做一些手脚,将代码隐藏起来;
再将原来存储函数的地方变成0;
别人就很难对代码做逆向分析了;
代码:
1 unsigned char code[] = //相当于两个int数相加的机器码
2 {
3 0x55,
4 0x8B, 0xEC,
5 0x83, 0xEC, 0x40,
6 0x53,
7 0x56,
8 0x57,
9 0x8D, 0x7D, 0xC0,
10 0xB9, 0x10, 0x00, 0x00, 0x00,
11 0xB8, 0xCC, 0xCC, 0xCC, 0xCC,
12 0xF3, 0xAB,
13 0x8B, 0x45, 0x08,
14 0x03, 0x45, 0x0C,
15 0x5F,
16 0x5E,
17 0x5B,
18 0x8B, 0xE5,
19 0x5D,
20 0xC3
21 };
22
23 void fun(){
24 typedef int (*Fun)(int,int);
25 Fun p = (int (*)(int,int))&code;
26 printf("%d",p(1,2)); //3
27 }