`
switchlau
  • 浏览: 52765 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

(转) 关于指针与函数的几点小结

阅读更多

http://bbs.chinaunix.net/viewthread.php?tid=993238&extra=page%3D1%26amp%3Bfilter%3Ddigest

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

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

 

#include<stdio.h>

void fun()
{
}

int main()
{
        printf("%p      %p      %p\n", &fun, fun, *fun);
        return 0;
}


 这三个值的结果是一样的. 其实对于最后的那个*fun, 即使前面加上很多个*号, 其结果也不变, 即**fun, ***fun的结果都是一样的. 对于这个问题, 因为之前讲过函数是一种
function-to-pointer方式, 其会自动转换成指针的类型, &fun是该函数的地址, 为指针类型, fun是一个函数, 会转换成其指针类型, 而对于*fun, 由于fun已经变成了指针类型, 指向这个函数, 所以*fun就是取这个地址的函数, 而又根据function-to-pointer, 该函数也转变成了一个指针, 所以以此类推, 这三个值的结果是相同的.

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

void f(int i)
{
        printf("i = %d\n", i);
}

int main()
{
        unsigned long add;
        add = (unsigned long)f;
        ((void (*)(int))add)(10);
        (*(void (*)(int))add)(20);
        return 0;
}


 使用(void (*)(int))的方式可以将一个地址转换成一个带int参数且没有返回值的函数的指针类型, 然后再去调用, 由于第1点中讲的function-to-pointer, 所以最后两条语句中加与不加那个*号效果都是一样的. 在嵌入式方面经常用到这种方式.

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

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

int min(int v1, int v2)
{
        return (v1 < v2 ? v1 : v2);
}

int sum(int v1, int v2)
{
        return (v1 + v2);
}

int main()
{
        int (*p[3])(int, int);
        p[0] = max;
        p[1] = min;
        p[2] = sum;

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

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

#include<stdio.h>
#include<stdlib.h>
int (*p())[10]
{
        int (*m)[10];
        int i;
        m = (int (*)[10])malloc(10 * sizeof(int));
        if (m == NULL)
        {
                printf("malloc error\n");
                exit(1);
        }
        for (i = 0; i < 10; i++)
                *(*m+i) = i+1;
        return m;
}

int main()
{
        int (*a)[10];
        int i;
        a = p();
        for (i = 0; i < 10; i++)
                printf("%d ", *(*a+i));
        printf("\ndone\n");
        return 0;
}


 这种方式中,int (*a)[10]是一个指向一维数组的一个指针, 而p()也是返回一个指向一维数组的一个指针.

5.返回一个函数指针的指针
对这个问题, signal()函数是最好的例子,
void (*signal (int signo, void (*func)(int)))(int);
 很多朋友刚开始看这个函数定义的时候是不太懂, 其实可以一步一步地慢慢看, 我以前是这样分析的, 希望能对大家有用.
int (*p)();
这是一个函数指针, p所指向的函数是一个不带任何参数, 并且返回值为int的一个函数.
int (*fun())();
这个式子与上面式子的区别在于用fun()代替了p,而fun()是一个函数,所以说就可以看成是fun()这个函数执行之后,它的返回值是一个函数指针,这个函数指针(其实就是上面的p)所指向的函数是一个不带任何参数,并且返回值为int的一个函数.所以说对于
void (*signal(int signo, void (*fun)(int)))(int);
  
就可以看成是signal()函数(它自己是带两个参数,一个为整型,一个为函数指针的函数), 而这个signal()函数的返回值也为一个函数指针,这个函数指针指向一个带一个整型参数,并且返回值为void的一个函数.
signal函数返回的其实是指向以前的信号处理程序的指针, 所以举一个例子来说明返回指向函数的指针的用法,如:
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>

void sig_fun2(int signo)
{
        printf("in sig_fun2:%d\n", signo);
}

void sig_fun1(int signo)
{
        printf("in sig_fun1:%d\n", signo);
}

int main()
{
        unsigned long i;
        if (signal(SIGUSR1, sig_fun1) == SIG_ERR)
        {
                printf("signal fun1 error\n");
                exit(1);
        }

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

        printf("done\n");
        return 0;
}
 


6. 使用函数指针作为参数的情况
在函数的参数中, 可能会带有一个函数指针, 这在signal()函数中是出现了的, 另外再写个例子如:

#include<stdio.h>

int max(int v1, int v2)
{
        return (v1 > v2 ? v1 : v2);
}

int min(int v1, int v2)
{
        return (v1 < v2 ? v1 : v2);
}

int sum(int v1, int v2)
{
        return (v1 + v2);
}

int fun(int a, int b, int (*call)(int, int))
{
        return (call(a, b));
}

int main()
{
        printf("max=%d\n", fun(1, 2, max));
        printf("min=%d\n", fun(3, 4, min));
        printf("sum=%d\n", fun(5, 6, sum));
        return 0;
}

 


其实在很多排序函数中就是使用的这个参数为函数指针的方式来进行调用的.

草草地总结了一下, 希望能有一些用.  

分享到:
评论

相关推荐

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar

    10.8 有关指针的数据类型和指针运算的小结 167 10.8.1 有关指针的数据类型的小结 167 10.8.2 指针运算的小结 167 10.8.3 void 指针类型 168 11 结构体与共用体 11.1 定义一个结构的一般形式 170 11.2 结构类型变量的...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar )

    10.8 有关指针的数据类型和指针运算的小结 167 10.8.1 有关指针的数据类型的小结 167 10.8.2 指针运算的小结 167 10.8.3 void 指针类型 168 11 结构体与共用体 11.1 定义一个结构的一般形式 170 11.2 结构类型变量的...

    VC++常用的共用函数100多页

    ◆将一个CWnd指针转换成一个控件ID(整数)注意用GetDlgItem()函数是从一个控件ID转换成一个CWnd的指针◆ 28 ◆如果在对话框中自定义一个消息的方法,假设对话框名为CXX◆ 28 ◆消息传递◆ 29 ◆在一个函数引用另外的...

    精通DirectX.3D图形与动画程序设计.pdf

     5.4.3 使用灯光的几点说明 5.5 深入理解光照计算模型 5.6 小结 第6章 纹理映射基础 6.1 基本概念 6.2 使用纹理 6.3 纹理过滤方式 6.3.1 最近点采样  6.3.2 线性纹理过滤 6.3.3 各项异性纹理过滤 6.3.4 多级渐进...

    数据结构与算法分析C描述第三版

     小结   练习   参考文献  第2章 算法分析   2.1 数学基础   2.2 模型   2.3 要分析的问题   2.4 运行时间计算   2.4.1 一个简单的例子   2.4.2 一般法则   2.4.3 最大子序列和问题的解...

    C语言入门经典(第4版)--源代码及课后练习答案

    1.10 小结 18 1.11 习题 18 第2章 编程初步 19 2.1 计算机的内存 19 2.2 什么是变量 21 2.3 存储数值的变量 21 2.3.1 整数变量 21 2.3.2 变量的命名 25 2.3.3 变量的使用 26 2.3.4 变量的初始化 28 2.3.5 ...

    数据结构与算法分析

     小结   练习   参考文献  第2章 算法分析   2.1 数学基础   2.2 模型   2.3 要分析的问题   2.4 运行时间计算   2.4.1 一个简单的例子   2.4.2 一般法则   2.4.3 最大子序列...

    Visual C++2010开发权威指南(共三部分).part1.rar

    2.9 小结 102 第3章 Visual C++ 2010 MFC菜单编程 103 3.1 菜单编程 103 3.1.1 创建菜单 104 3.1.2 创建菜单热键 115 3.1.3 标记菜单 115 3.1.4 给菜单加入图标 119 3.1.5 禁用菜单 121 3.1.6 移除与加载菜单 127 ...

    传智播客_C++基础课程讲义_v1.0.7

    附录1:C++语言对C语言扩充和增强的几点具体体现 6 附录2:C语言register关键字—最快的关键字 6 一、皇帝身边的小太监----寄存器 6 2、类和对象 6 1前言 6 2类和对象 6 2.1 基本概念 6 2.2类的封装 6 2.3 C++面向...

    Windows内核安全与驱动开发光盘源码

    9.4.7 小结 152 9.5 FAT12/16磁盘卷初始化 152 9.5.1 磁盘卷结构简介 152 9.5.2 Ramdisk对磁盘的初始化 154 9.6 驱动中的请求处理 160 9.6.1 请求的处理 160 9.6.2 读/写请求 160 9.6.3 DeviceIoControl请求 ...

    Windows内核安全驱动开发(随书光盘)

    9.4.7 小结 152 9.5 FAT12/16磁盘卷初始化 152 9.5.1 磁盘卷结构简介 152 9.5.2 Ramdisk对磁盘的初始化 154 9.6 驱动中的请求处理 160 9.6.1 请求的处理 160 9.6.2 读/写请求 160 9.6.3 DeviceIoControl请求 ...

    寒江独钓-Windows内核安全编程(高清完整版).part3

    5.4.7 小结 111 5.5 FAT12/16磁盘卷初始化 111 5.5.1 磁盘卷结构简介 111 5.5.2 Ramdisk对磁盘的初始化 113 5.6 驱动中的请求处理 119 5.6.1 请求的处理 119 5.6.2 读/写请求 120 5.6.3 DeviceIoControl请求 122 5.7...

    寒江独钓-Windows内核安全编程(高清完整版).part1

    5.4.7 小结 111 5.5 FAT12/16磁盘卷初始化 111 5.5.1 磁盘卷结构简介 111 5.5.2 Ramdisk对磁盘的初始化 113 5.6 驱动中的请求处理 119 5.6.1 请求的处理 119 5.6.2 读/写请求 120 5.6.3 DeviceIoControl请求 122 5.7...

    寒江独钓-Windows内核安全编程(高清完整版).part2

    5.4.7 小结 111 5.5 FAT12/16磁盘卷初始化 111 5.5.1 磁盘卷结构简介 111 5.5.2 Ramdisk对磁盘的初始化 113 5.6 驱动中的请求处理 119 5.6.1 请求的处理 119 5.6.2 读/写请求 120 5.6.3 DeviceIoControl请求 122 5.7...

    寒江独钓-Windows内核安全编程(高清完整版).part7

    5.4.7 小结 111 5.5 FAT12/16磁盘卷初始化 111 5.5.1 磁盘卷结构简介 111 5.5.2 Ramdisk对磁盘的初始化 113 5.6 驱动中的请求处理 119 5.6.1 请求的处理 119 5.6.2 读/写请求 120 5.6.3 DeviceIoControl请求 122 5.7...

    寒江独钓-Windows内核安全编程(高清完整版).part4

    5.4.7 小结 111 5.5 FAT12/16磁盘卷初始化 111 5.5.1 磁盘卷结构简介 111 5.5.2 Ramdisk对磁盘的初始化 113 5.6 驱动中的请求处理 119 5.6.1 请求的处理 119 5.6.2 读/写请求 120 5.6.3 DeviceIoControl请求 122 5.7...

Global site tag (gtag.js) - Google Analytics