Chinaunix

标题: 关于指针与函数的几点小结 [打印本页]

作者: scutan    时间: 2007-09-19 16:14
标题: 关于指针与函数的几点小结
本帖最后由 JohnBull 于 2011-05-08 21:26 编辑

心血来潮, 想对函数指针的几个用法小结一下, 都是平常容易见到的, 如果还有其它不觉的用法也请朋友们不吝赐教.

若有错误之处,还请指正.

1. 首先,在C语言中函数是一种function-to-pointer的方式,即对于一个函数,会将其自动转换成指针的类型.如:

  1. #include<stdio.h>

  2. void fun()
  3. {
  4. }

  5. int main()
  6. {
  7.         printf("%p      %p      %p\n", &fun, fun, *fun);
  8.         return 0;
  9. }
复制代码
这三个值的结果是一样的. 其实对于最后的那个*fun, 即使前面加上很多个*号, 其结果也不变, 即**fun, ***fun的结果都是一样的. 对于这个问题, 因为之前讲过函数是一种
function-to-pointer方式, 其会自动转换成指针的类型, &fun是该函数的地址, 为指针类型, fun是一个函数, 会转换成其指针类型, 而对于*fun, 由于fun已经变成了指针类型, 指向这个函数, 所以*fun就是取这个地址的函数, 而又根据function-to-pointer, 该函数也转变成了一个指针, 所以以此类推, 这三个值的结果是相同的.

2. 如何调用一个地址上的函数
  如果知道了一个函数所在的地址, 可以将其强制转化成某一种类型的函数指针, 然后再根据这个指针去调用这个地址的函数. 如:

  1. #include<stdio.h>

  2. void f(int i)
  3. {
  4.         printf("i = %d\n", i);
  5. }

  6. int main()
  7. {
  8.         unsigned long add;
  9.         add = (unsigned long)f;
  10.         ((void (*)(int))add)(10);
  11.         (*(void (*)(int))add)(20);
  12.         return 0;
  13. }
复制代码
使用(void (*)(int))的方式可以将一个地址转换成一个带int参数且没有返回值的函数的指针类型, 然后再去调用, 由于第1点中讲的function-to-pointer, 所以最后两条语句中加与不加那个*号效果都是一样的. 在嵌入式方面经常用到这种方式.

3. 函数指针数组的用法.
有时候需要定义一个数组, 其内容为一系列的函数指针, 然后对其进行调用, 如:

  1. #include<stdio.h>
  2. int max(int v1, int v2)
  3. {
  4.         return (v1 > v2 ? v1 : v2);
  5. }

  6. int min(int v1, int v2)
  7. {
  8.         return (v1 < v2 ? v1 : v2);
  9. }

  10. int sum(int v1, int v2)
  11. {
  12.         return (v1 + v2);
  13. }

  14. int main()
  15. {
  16.         int (*p[3])(int, int);
  17.         p[0] = max;
  18.         p[1] = min;
  19.         p[2] = sum;

  20.         printf("p[0] = %d\n", (p[0])(3, 5));
  21.         printf("p[1] = %d\n", (p[1])(4, 6));
  22.         printf("p[2] = %d\n", (p[2])(1, 2));
  23.         return 0;
  24. }
复制代码
虽然感觉这种方法有点累赘, 但是也算是一种使用的方式, 所以介绍一下.

4.返回一个指向数组的指针的方式
可以让函数返回一个指向数组的一个指针, 如:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int (*p())[10]
  4. {
  5.         int (*m)[10];
  6.         int i;
  7.         m = (int (*)[10])malloc(10 * sizeof(int));
  8.         if (m == NULL)
  9.         {
  10.                 printf("malloc error\n");
  11.                 exit(1);
  12.         }
  13.         for (i = 0; i < 10; i++)
  14.                 *(*m+i) = i+1;
  15.         return m;
  16. }

  17. int main()
  18. {
  19.         int (*a)[10];
  20.         int i;
  21.         a = p();
  22.         for (i = 0; i < 10; i++)
  23.                 printf("%d ", *(*a+i));
  24.         printf("\ndone\n");
  25.         return 0;
  26. }
复制代码
这种方式中,int (*a)[10]是一个指向一维数组的一个指针, 而p()也是返回一个指向一维数组的一个指针.

5.返回一个函数指针的指针
对这个问题, signal()函数是最好的例子,

  1. void (*signal (int signo, void (*func)(int)))(int);
复制代码
很多朋友刚开始看这个函数定义的时候是不太懂, 其实可以一步一步地慢慢看, 我以前是这样分析的, 希望能对大家有用.
int (*p)();
这是一个函数指针, p所指向的函数是一个不带任何参数, 并且返回值为int的一个函数.
int (*fun())();
这个式子与上面式子的区别在于用fun()代替了p,而fun()是一个函数,所以说就可以看成是fun()这个函数执行之后,它的返回值是一个函数指针,这个函数指针(其实就是上面的p)所指向的函数是一个不带任何参数,并且返回值为int的一个函数.所以说对于

  1. void (*signal(int signo, void (*fun)(int)))(int);
复制代码
就可以看成是signal()函数(它自己是带两个参数,一个为整型,一个为函数指针的函数), 而这个signal()函数的返回值也为一个函数指针,这个函数指针指向一个带一个整型参数,并且返回值为void的一个函数.
signal函数返回的其实是指向以前的信号处理程序的指针, 所以举一个例子来说明返回指向函数的指针的用法,如:

  1. #include<signal.h>
  2. #include<stdlib.h>
  3. #include<stdio.h>

  4. void sig_fun2(int signo)
  5. {
  6.         printf("in sig_fun2:%d\n", signo);
  7. }

  8. void sig_fun1(int signo)
  9. {
  10.         printf("in sig_fun1:%d\n", signo);
  11. }

  12. int main()
  13. {
  14.         unsigned long i;
  15.         if (signal(SIGUSR1, sig_fun1) == SIG_ERR)
  16.         {
  17.                 printf("signal fun1 error\n");
  18.                 exit(1);
  19.         }

  20.         (signal(SIGUSR1, sig_fun2))(30);

  21.         printf("done\n");
  22.         return 0;
  23. }
复制代码
6. 使用函数指针作为参数的情况
在函数的参数中, 可能会带有一个函数指针, 这在signal()函数中是出现了的, 另外再写个例子如:

  1. #include<stdio.h>

  2. int max(int v1, int v2)
  3. {
  4.         return (v1 > v2 ? v1 : v2);
  5. }

  6. int min(int v1, int v2)
  7. {
  8.         return (v1 < v2 ? v1 : v2);
  9. }

  10. int sum(int v1, int v2)
  11. {
  12.         return (v1 + v2);
  13. }

  14. int fun(int a, int b, int (*call)(int, int))
  15. {
  16.         return (call(a, b));
  17. }

  18. int main()
  19. {
  20.         printf("max=%d\n", fun(1, 2, max));
  21.         printf("min=%d\n", fun(3, 4, min));
  22.         printf("sum=%d\n", fun(5, 6, sum));
  23.         return 0;
  24. }
复制代码
其实在很多排序函数中就是使用的这个参数为函数指针的方式来进行调用的.

草草地总结了一下, 希望能有一些用.  
作者: zx_wing    时间: 2007-09-19 16:21
原帖由 scutan 于 2007-9-19 16:14 发表
心血来潮, 想对函数指针的几个用法小结一下, 都是平常容易见到的, 如果还有其它不觉的用法也请朋友们不吝赐教.

若有错误之处,还请指正.

1. 首先,在C语言中函数是一种function-to-pointer的方式,即对于一个 ...

赞lz的热心。
对于函数指针数组,其实这是很常用的。一个常见的例子就是linux下的syscall table了。虽然是用汇编写的,但实质是一样的
作者: scutan    时间: 2007-09-19 16:27
原帖由 zx_wing 于 2007-9-19 16:21 发表

赞lz的热心。
对于函数指针数组,其实这是很常用的。一个常见的例子就是linux下的syscall table了。虽然是用汇编写的,但实质是一样的


嗯. 我写的时候倒把这点给忘了, 通过系统调用号来找到其函数入口地址. 谢谢提醒.
作者: ypxing    时间: 2007-09-19 16:30
不错,赞一个
作者: ruoyisiyu    时间: 2007-09-19 17:00
scutan的热心值得肯定 ,可是你的blog咋就没更新上呢。。
作者: 飞灰橙    时间: 2007-09-19 17:00
楼主可不可以再这样介绍下:
1. 用 typedef 定义一个函数指针类型
2. 把这个类型的变量当成普通的变量使用
    既然是普通变量,什么数组啊,返回值啊,都不需要讨论了。
作者: scutan    时间: 2007-09-19 17:04
原帖由 ruoyisiyu 于 2007-9-19 17:00 发表
scutan的热心值得肯定 ,可是你的blog咋就没更新上呢。。


呵呵, 刚刚才写好.
作者: lan_wjz    时间: 2007-09-19 17:27
可以将 ((void (*)(int))add)(10) 转换为 :
typedef void (*FUNC_ADDR)(int);
调用 :((FUNC_ADDR)add)(10);
这样更清楚一些
作者: Godbach    时间: 2007-09-19 17:30
标题: 回复 #7 scutan 的帖子
辛苦了!!
学习了!
作者: scutan    时间: 2007-09-19 17:59
谢谢刚才大家的提醒, 我对刚才的几个代码使用typedef作了如下的修改, 看起来简便一些.

标号与上面的标号相同.

2.

  1. #include<stdio.h>

  2. typedef void (*pfun)(int);

  3. void f(int i)
  4. {
  5.         printf("i = %d\n", i);
  6. }

  7. int main()
  8. {
  9.         unsigned long add;
  10.         add = (unsigned long)f;
  11.         ((pfun)add)(10);
  12.         (*(pfun)add)(20);
  13.         return 0;
  14. }
复制代码


3.

  1. #include<stdio.h>

  2. typedef int (*pfun)(int, int);

  3. int max(int v1, int v2)
  4. {
  5.         return (v1 > v2 ? v1 : v2);
  6. }

  7. int min(int v1, int v2)
  8. {
  9.         return (v1 < v2 ? v1 : v2);
  10. }

  11. int sum(int v1, int v2)
  12. {
  13.         return (v1 + v2);
  14. }

  15. int main()
  16. {
  17.         pfun p[3];
  18.         p[0] = max;
  19.         p[1] = min;
  20.         p[2] = sum;

  21.         printf("p[0] = %d\n", (p[0])(3, 5));
  22.         printf("p[1] = %d\n", (p[1])(4, 6));
  23.         printf("p[2] = %d\n", (p[2])(1, 2));
  24.         return 0;
  25. }
复制代码


4.

  1. #include<stdio.h>
  2. #include<stdlib.h>

  3. typedef int (*pfun)[10];

  4. pfun p()
  5. {
  6.         pfun m;
  7.         int i;
  8.         m = (pfun)malloc(10 * sizeof(int));
  9.         if (m == NULL)
  10.         {
  11.                 printf("malloc error\n");
  12.                 exit(1);
  13.         }
  14.         for (i = 0; i < 10; i++)
  15.                 *(*m+i) = i+1;
  16.         return m;
  17. }

  18. int main()
  19. {
  20.         pfun a;
  21.         int i;
  22.         a = p();
  23.         for (i = 0; i < 10; i++)
  24.                 printf("%d ", *(*a+i));
  25.         printf("\ndone\n");
  26.         return 0;
  27. }
复制代码


5.

  1. #include<stdio.h>

  2. typedef int (*pfun)(int, int);

  3. int max(int v1, int v2)
  4. {
  5.         return (v1 > v2 ? v1 : v2);
  6. }

  7. int min(int v1, int v2)
  8. {
  9.         return (v1 < v2 ? v1 : v2);
  10. }

  11. int sum(int v1, int v2)
  12. {
  13.         return (v1 + v2);
  14. }

  15. int fun(int a, int b, pfun call)
  16. {
  17.         return (call(a, b));
  18. }

  19. int main()
  20. {
  21.         printf("max=%d\n", fun(1, 2, max));
  22.         printf("min=%d\n", fun(3, 4, min));
  23.         printf("sum=%d\n", fun(5, 6, sum));
  24.         return 0;
  25. }
复制代码

作者: converse    时间: 2007-09-19 18:02
没有细看,鼓励一下原创.
作者: albcamus    时间: 2007-09-19 18:33
很受益,谢谢!
作者: cugb_cat    时间: 2007-09-19 18:47
恩,看不懂的可以去翻翻《C专家编程》的第三章(好像是这一章)
作者: ddvv    时间: 2007-09-19 21:28
写得很好,顶你~
第4条有点不明白,为什么访问int (*a)[10]要用*(*a+i)的方式呢?int (*a)[10]与a[10]有什么不同吗?用*(a+1)不可以?
作者: ypxing    时间: 2007-09-19 21:30
不可以
a+1移动sizeof(int)*10
*a+1移动sizeof(int)

原帖由 ddvv 于 2007-9-19 21:28 发表
写得很好,顶你~
第4条有点不明白,为什么访问int (*a)[10]要用*(*a+i)的方式呢?int (*a)[10]与a[10]有什么不同吗?用*(a+1)不可以?

作者: ddvv    时间: 2007-09-19 21:35
原帖由 ypxing 于 2007-9-19 21:30 发表
不可以
a+1移动sizeof(int)*10
*a+1移动sizeof(int)


这么说来,a是int [10]类型的指针咯?在内存里面是怎么放的?*a里面放的是什么?

[ 本帖最后由 ddvv 于 2007-9-19 21:38 编辑 ]
作者: scutan    时间: 2007-09-19 21:41
原帖由 ddvv 于 2007-9-19 21:28 发表
写得很好,顶你~
第4条有点不明白,为什么访问int (*a)[10]要用*(*a+i)的方式呢?int (*a)[10]与a[10]有什么不同吗?用*(a+1)不可以?


int (*a)[10]表示的是指向一个一维数组的指针, 该一维数组是一个int [10]类型, 一般用其指向一个二维数组的首地址, 如:

  1. int arr[4][10];
  2. a = arr;
复制代码

这种方式, 此时可以将arr看成4个数组, 每个数组都是一个10个整型的一个数组, 此时a就指向了其中的第0个以10个整型为单位的数组, 你要访问arr[1][3]; 就要使用a[1][3]的方式,
表示的是第1行第3的这个数.
*(a+1) 相当于是a[1], 其实(a+1)与*(a+1)的值是一样的, 因为(a+1)表示的是a[1]的地址, 而*(a+1)表示的是a[1]的值, 其实也就是a[1][0]的地址, 而它们是相同的地址.
对于这个问题, 我觉得谭浩强上面那儿讲得比较清晰, 可以参考一下!
作者: scutan    时间: 2007-09-19 21:45
另外, a, *a这两个看待的对象不一样.
a+1跳过的是10个整型的距离而*a+1则是一个整型长度的距离. *a就已经使其减少一维了.
作者: ddvv    时间: 2007-09-19 21:45
恩,关于多为数组我晓得,只是不理解int (*a)[10]这个声明。
如此看来,a就相当与一个指针的指针,也就是int **a了,不知道这样理解对不对?
作者: ypxing    时间: 2007-09-19 21:46
不对

原帖由 ddvv 于 2007-9-19 21:45 发表
恩,关于多为数组我晓得,只是不理解int (*a)[10]这个声明。
如此看来,a就相当与一个指针的指针,也就是int **a了,不知道这样理解对不对?

作者: ddvv    时间: 2007-09-19 21:48
原帖由 ypxing 于 2007-9-19 21:46 发表
不对


哪里不对了?

说一下题外话,CU的数据库是不是出现问题了,我想开通博客老是出现数据库错误~

[ 本帖最后由 ddvv 于 2007-9-19 21:50 编辑 ]
作者: scutan    时间: 2007-09-19 21:52
原帖由 ddvv 于 2007-9-19 21:45 发表
恩,关于多为数组我晓得,只是不理解int (*a)[10]这个声明。
如此看来,a就相当与一个指针的指针,也就是int **a了,不知道这样理解对不对?


不是的
int **a, a此时为一个指针, 其指向的地址中的内容也为一个指针类型, 而int (*a)[]这种方式, a是一个指针, 但是其指向的内容是一个int类型.
对于这种情况, 很容易犯的错误就是在函数调用时.

  1. int a[10][4];
  2. void fun(int **p);
  3. fun(a);
复制代码

这种使用方式.
作者: ddvv    时间: 2007-09-19 21:57

哎,混乱了~看来是基础没学好,我再去找找相关的资料来看看吧。。。
作者: ypxing    时间: 2007-09-19 22:00
int a[10];与int *a;是有区别的
sizeof, &a+1意义都不相同

同理int (*a)[10];与int *(*a);也是不一样的

原帖由 ddvv 于 2007-9-19 21:45 发表
恩,关于多为数组我晓得,只是不理解int (*a)[10]这个声明。
如此看来,a就相当与一个指针的指针,也就是int **a了,不知道这样理解对不对?

作者: wzabcd1234    时间: 2007-09-19 22:18
受益了,不过有一个问题没有理解。
第一个程序中&fun和fun的地址为什么是相同的啊?

比如下面这个程序:
int main()
{
    int a  = 13;
    int *p = &a;
    printf("%p\n", p);
    printf("%p\n", &p);
    printf("%p\n", &a);

    return 0;
}

结果是:
<abcd:/home/abcd>./a.out
0xbfa19b40
0xbfa19b3c
0xbfa19b40
作者: aitilang    时间: 2007-09-19 22:25
标题: 回复 #17 scutan 的帖子
二维数组行指针。。。
作者: aitilang    时间: 2007-09-19 22:29
原帖由 wzabcd1234 于 2007-9-19 22:18 发表
受益了,不过有一个问题没有理解。
第一个程序中&fun和fun的地址为什么是相同的啊?

比如下面这个程序:
int main()
{
    int a  = 13;
    int *p = &a;
    printf("%p\n", p);
    printf("%p\n", ...


&fun是该函数的地址, 为指针类型, fun是一个函数, 会转换成其指针类型

作者: wzabcd1234    时间: 2007-09-19 22:37
嗯,谢谢了,是我想复杂了。
作者: chinesedragon    时间: 2007-09-19 22:58
又学到不少东西~
作者: cugb_cat    时间: 2007-09-19 23:36
原帖由 ddvv 于 2007-9-19 21:57 发表

哎,混乱了~看来是基础没学好,我再去找找相关的资料来看看吧。。。

C专家编程上有很好的解释,建议看看
作者: ddvv    时间: 2007-09-19 23:48
现在终于明白了。CSDN上面找到一边专门讲指针的文章,有点长,但是讲得通俗易懂。
推荐一下:
http://community.csdn.net/Expert/TopicView3.asp?id=5290998
作者: Vinge    时间: 2007-09-20 08:18
给搂主顶一个,1那条我还真不知道是那么一回事!
5那条看书时也犯晕。
搂主讲解的不错!
作者: guhan010    时间: 2007-09-20 08:24
搞这么复杂,很少用到的啊.
其实,用简单的东西解释一种复杂更好点.
作者: 1980116    时间: 2007-09-20 09:05
标题: 回复 #1 scutan 的帖子
太感谢了,正为这几个问题烦愁呢,这里的确有高人啊
作者: ghostbb440    时间: 2007-09-20 09:38
楼主热心人啊,对我们这些刚入门的人很受益
作者: beyondfly    时间: 2007-09-20 09:39
楼主这个头像不错,不知道人长得怎么样????
作者: lanneret_sky    时间: 2007-09-20 09:48
总结的不错
作者: rainballdh    时间: 2007-09-20 11:30
支持一下,受益了谢谢
作者: hb12112    时间: 2007-09-20 15:08
受益了。谢谢了。
作者: linginfanta    时间: 2007-09-20 16:39
写得很好,收藏。
作者: lpzgbd    时间: 2007-09-20 20:56
非常感谢,长见识了。
c中的指针太灵活了
如果楼主不介意的话,我想把此文转载了。。
作者: vikingrex    时间: 2007-09-21 11:48
标题: 牛。。。
牛。。。
下面几句不大明白。。
能不能解释一下。。
      add = (unsigned long)f;
        ((void (*)(int))add)(10);
        (*(void (*)(int))add)(20);
第二个例子里面的代码。
作者: arnina    时间: 2007-09-21 13:37
原帖由 ddvv 于 2007-9-19 23:48 发表
现在终于明白了。CSDN上面找到一边专门讲指针的文章,有点长,但是讲得通俗易懂。
推荐一下:
http://community.csdn.net/Expert/TopicView3.asp?id=5290998

下面是他的观点:
  “下面总结一下数组的数组名的问题。声明了一个数组TYPEarray[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是 TYPE[n];第二,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的”
这句好象不对吧:
该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的
作者: scutan    时间: 2007-09-21 13:41
原帖由 arnina 于 2007-9-21 13:37 发表

下面是他的观点:
  “下面总结一下数组的数组名的问题。声明了一个数组TYPEarray[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是 TYPE[n];第二,它是一个指针,该指针的类型是TY ...


嗯. 我也觉得这句话是不准确的, TYPEarray其实相当于是一个标记, 表示了某段内存区域.
相当于int i; 给这个变量分配了sizeof(int)长度的内存, 但是却不存在为 i 这个名字再进行什么分配之类的说法!
作者: jinq0123    时间: 2007-09-21 15:12
这三个值的结果是一样的. 其实对于最后的那个*fun, 即使前面加上很多个*号, 其结果也不变, 即**fun, ***fun的结果都是一样的


学习了。
作者: wangyq126    时间: 2007-09-24 21:53
学习了
作者: liu1061    时间: 2007-10-01 11:56
thx,学习了!!!!
作者: shangyilong    时间: 2007-10-16 16:08
标题: 回复 #1 scutan 的帖子
支持原创
作者: francybright    时间: 2007-10-16 21:52
赞!!!
作者: ruibob    时间: 2007-12-01 18:10
提示: 作者被禁止或删除 内容自动屏蔽
作者: hotjuly    时间: 2007-12-01 20:11
老外写的

http://hi.baidu.com/hotjuly/blog ... 51caaa8226ac95.html
作者: xi2008wang    时间: 2007-12-01 21:54
今天我学指针头都大了
什么指针,指针数组,数组指针,字符串指针,函数指针,指针函数,空指针,指针指针,void指针
现在还有什么函数指针数组........
天啊!!!!!!!:em11: :em11: :em11:
作者: ivhb    时间: 2007-12-03 18:50
原帖由 ddvv 于 2007-9-19 21:45 发表
恩,关于多为数组我晓得,只是不理解int (*a)[10]这个声明。
如此看来,a就相当与一个指针的指针,也就是int **a了,不知道这样理解对不对?

相当于
int a[10];
&a  <=> int (*a) [10]
作者: grasspower    时间: 2008-01-16 11:09
up!又要学习~
作者: cheney_lang    时间: 2008-01-16 20:12
谢谢,好好向你学习
作者: davycu    时间: 2008-01-18 14:30
收藏学习
作者: talent_t    时间: 2008-01-18 16:24
请问LZ

int (*p())[10]                 //这能不能解释一下,谢谢!!
{
        int (*m)[10];

.
.
.
.
.
作者: scutan    时间: 2008-01-18 20:26
原帖由 talent_t 于 2008-1-18 16:24 发表
请问LZ

int (*p())[10]                 //这能不能解释一下,谢谢!!
{
        int (*m)[10];

.
.
.
.
.


int (*m)[10] 中的m是一个指针, 这个指针是指向一个数组的, 这个数组就是int [10]的一个数组.
而int (*p())[10]的话则为: p()为一个一个函数, 它返回的相当于就是一个指向int[10]这个数组的一个指针.

呵呵, 其实对于指针我现在也不怎么关注了. 觉得用到的时候再看看就行.
作者: gosapphire    时间: 2008-01-31 23:24
好帖,再次重新看一篇,又有收获。
作者: 天下洪荒    时间: 2008-02-07 17:32
学习中~~
作者: mik    时间: 2008-02-08 01:03
再加一个知识点:

  1. void foo()
  2. {
  3.        printf("hello\n");
  4. }

  5. int main()
  6. {
  7.       void (*pf)() = foo;

  8.       pf();

  9.       return 0;
  10. }
复制代码


类似以上通过指针间接调用的代码,在x86上,gcc通常是产生类似以下这种调用的编码方式:

  1. movl $foo, %eax
  2. call    *%eax
复制代码


x86 只能通过间接调用函数,并无直接调用某个目标地址的指令编码,当然也可以像以下这种调用
movl $foo,%eax
movl %eax, -4(%ebp)                // 通过某一变量进行间接调用
call  *-4(%ebp)

以下这种调用方式是错误的,x86并不支持这种编码,只能直接远程直接调用,如: call 0040:00401240
  1. call $foo
复制代码




明白以上一点,在写 shellcode 代码时,是有用处的,但一般人不会用到以上这一点知识。

例:
void foo()
{
       printf("hello\n");
}

int main()
{
      char c[7];
      int pf = (int)foo;                             

      c[0] = 0xff;
      c[1] = 0x15;
      *(((int*)&c[2])) = (int)&pf;
      c[6] = 0xc3;
  
      ((void (*)())&c)();

      return 0;
}


以上代码,可以算是极简单一段 shellcode, 利用 stack 的一个数组执行一段程序。

蓝色部分:写入被调用函数间接存放的变量地址。
          若直接写入函数地址,则会出错,因为 call 指令只能做到 call [fun] 而不能做到 call fun

红色部分:调用数组时,要用&间接方式,若 ((void (*)())c)() 则同样会出错。

[ 本帖最后由 mik 于 2008-2-8 01:07 编辑 ]
作者: loveoov    时间: 2008-05-20 10:55
标题: 回复 #1 scutan 的帖子
谢谢了
收藏~~~~
作者: militala    时间: 2008-05-20 19:58
提示: 作者被禁止或删除 内容自动屏蔽
作者: dianlongliu    时间: 2008-05-21 20:53
mark
赞一个.
作者: zhongsishun    时间: 2008-05-23 00:27
标题: 回复 #1 scutan 的帖子
辛苦了..
我刚学的,..
作者: ghosthjf    时间: 2008-05-23 11:15
顶一个。
作者: facetosky    时间: 2008-05-23 13:53
感谢LZ这么热心,另外也感谢14的提问和17楼的回答,太收益了,谢谢谢谢!:wink:
作者: goreycn    时间: 2008-05-27 17:17
太强,收藏起来研究.
另外,例子里是不是少了free.
不过不是研究重点,呵呵.
作者: lipingtababa    时间: 2008-05-27 18:48
收藏可不可以?
作者: fox_chu    时间: 2008-05-27 21:41
标题: 回复 #1 scutan 的帖子
学习!
支持
作者: zhhsboy    时间: 2008-05-30 17:25
mark
作者: jeepenin    时间: 2008-06-16 15:52
谢谢楼主
作者: lintao886    时间: 2008-06-22 13:48
受教了,谢谢了
作者: system888net    时间: 2008-06-22 18:20
不错,顶一下.
作者: zhangsuozhu    时间: 2010-08-26 18:00
好贴
作者: 蓝色神话    时间: 2010-08-26 18:48
非常不错的帖子,总结的很好。
作者: ming4098    时间: 2010-08-26 22:01
mark
作者: zjykzk    时间: 2010-11-13 12:17
本帖最后由 zjykzk 于 2010-11-13 14:09 编辑

请教一下:

1.typedef void Func(int);

2.typedef void (*Func1)(int);

1和2有什么区别?

我有一个函数
void func(int i) {}

为什么
Func f1 = func;
这样使用的错误的?

而,
Func *f = func;
这样可以?

谢谢!
作者: jhui66    时间: 2010-12-05 12:53
赞一个,不错
作者: bangziFnst    时间: 2010-12-24 11:17
请教一下:

1.typedef void Func(int);

2.typedef void (*Func1)(int);

1和2有什么区别?

我有 ...
zjykzk 发表于 2010-11-13 12:17



typedef void Func(int);         ----->Func 表示一个带有int型参数 返回值为空的函数
typedef void (*Func1)(int);        ----->Func1表示函数指针,此指针指向一个带有int型参数 返回值为空的函数

Func f1 = func;                ------> Func f1相当于定义了一个函数void f1(int);而函数的标示符f1表示的是常量,常量不能充当左值;所以错误。
Func *f1 = func;                ------->f1是一个指针变量,表示指向Func类型的指针,里面存储的是func函数的地址。所以正确。
P.S:只要你写的东东能被预处理器,编译器,汇编器,链接器识别,就好. :)
作者: zjykzk    时间: 2011-05-07 10:15
回复 80# bangziFnst


    受教了,谢谢




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2