相关文章推荐
逆袭的哑铃  ·  Android ...·  1 年前    · 

2. 函数指针 和 指针函数





(1) 指针函数



概念 : 函数返回的结果是一个地址, 即返回的是一个指针, 这个函数就是指针函数;




指针函数格式 : 类型说明符 *函数名(参数列表);


-- 示例 : char *getchar(void);


-- 格式说明 : char * 表示函数返回值是指针, 调用这个函数, 返回一个指针指向的char类型;




运算符优先级 : 指针函数有两个运算符 * 和 (), ()的优先级 大于 *, 因此函数名首先和 () 结合, 然后在和 * 结合;






(2) 函数指针



概念 : 函数指针指向了函数的地址, 该指针可以调用函数;



函数指针格式 : 类型说明符 (*指针名)(参数列表);


-- 示例 : char (*getcahr)(void);




运算符优先级 : * 和 指针名 先结合, 然后在与参数列表结合;




函数指针使用 :


-- 声明函数指针 : void (*getchar)(), 声明函数指针需要带上参数列表;


-- 为函数指针赋值 : getchar = &get_char 或者 getchar = get_char 两种方法, & 可有可无;


-- 调用函数指针方法 : (*get_char)();






(3) 使用函数指针示例



示例需求 :


-- 获取字符串数组 : 从标准输入流中读取字符串数据, 将字符串放入字符串数组 char **;


-- 可选参数 : -n, 如果有可选参数, 就是按照数值顺序排序, 否则按照字典顺序排序;




代码 :



/*************************************************************************
    > File Name: method_pointer_sort.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: Sat 22 Mar 2014 11:45:47 PM CST
 ************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//定义排序字符串最大个数
#define MAXLINES 50
//每个字符串最多50个元素
#define MAXLEN 50





    
//定义一个 有MAXLINES 个 元素的数组, 数组中的元素师字符串, 即char类型指针
char *lineptr[MAXLINES];
 * 声明函数指针
int (*p_get_line)(char *, int);
int (*p_read_lines)(char **, int);
void (*p_write_lines)(char **, int);
void (*p_q_sort)(void **, int, int, int (*)(void *, void*));
 * 声明函数, 如果直接使用这些函数, 即使函数定义在主函数后面也不会出错
 * 如果要将函数赋值给函数指针, 需要提前声明这些函数
int get_line(char *, int);
int read_lines(char **, int);
void write_lines(char **, int);
void q_sort(void *v[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int main(int argc, char **argv)
        char line[MAXLEN];
        int len, nlines, numberic = 0;
        p_read_lines = read_lines;
        p_write_lines = write_lines;
        p_q_sort = q_sort;
        //如果参数中含有 -n 说明这是按照数值顺序排序, 否则就按照字典顺序排序
        if(argc > 1 && strcmp(argv[1], "-n") == 0)
                numberic = 1;
        if((nlines = (*p_read_lines)(lineptr, MAXLINES)) >= 0)
                 * 注意 : 
                 *   使用 ? : 表达式选择 函数指针, 函数指针类型转换的时候, 为每个选项都添加转换
                 *   如果只转换 ? : 结果, 会报出警告
                (*p_q_sort)((void **)lineptr, 0, nlines - 1, numberic ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp);
                (*p_write_lines)(lineptr, nlines);





    
                return 0;
                printf("error \n");
                return 1;
//从标准输入流中读取数据, 放入字符串中
int get_line(char *line, int max)
        int i, c;
        for(i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i ++)
                *(line + i) = c;
        *(line + i) = '\0';
        return i;
//从标准输入流中读取数据, 放入字符串数组
int read_lines(char *lineptr[], int max)
        int len, nlines = 0;
        char *p, line[MAXLEN];
        while((len = get_line(line, MAXLEN)) > 0)
                if(nlines >= MAXLINES || (p = malloc(sizeof(char) * (len + 1))) == NULL)
                        return -1;
                        strcpy(p, line);
                        *(lineptr + nlines) = p;
                        nlines++;
        return nlines;
//将字符串数组中的元素打印出来
void write_lines(char *lineptr[], int nlines)
        int i;
        for(i = 0; i < nlines; i++)
                printf("the %d char sequence is : %s \n", i, *(lineptr + i));
//数值比较
int numcmp(char *s1, char *s2)
        double v1, v2;
        v1 = atof(s1);





    
        v2 = atof(s2);
        if(v1 < v2)
                return -1;
        else if(v1 > v2)
                return 1;
        else if(v1 == v2)
                return 0;
//交换数组中 i j 元素
void swap(void *v[], int i, int j)
        void *temp;
        temp = *(v + i);
        *(v + i) = *(v + j);
        *(v + j) = temp;
//排序方法
void q_sort(void *v[], int left, int right, int (*comp)(void *, void *))
        int i, last;
        if(left >= right)
                return;
        swap(v, left, (left + right)/2);
        last = left;
        for(i = last + 1; i <= right; i++)
                if((*comp)(v[i], v[left]) < 0)
                        swap(v, ++last, i);
        swap(v, left, last);
        q_sort(v, left, last - 1, comp);
        q_sort(v, last + 1, right, comp);
}


执行结果 :



/*************************************************************************





    
    > File Name: method_pointer_sort.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: Sat 22 Mar 2014 11:45:47 PM CST
 ************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//定义排序字符串最大个数
#define MAXLINES 50
//每个字符串最多50个元素
#define MAXLEN 50
//定义一个 有MAXLINES 个 元素的数组, 数组中的元素师字符串, 即char类型指针
char *lineptr[MAXLINES];
 * 声明函数指针
int (*p_get_line)(char *, int);
int (*p_read_lines)(char **, int);
void (*p_write_lines)(char **, int);
void (*p_q_sort)(void **, int, int, int (*)(void *, void*));
 * 声明函数, 如果直接使用这些函数, 即使函数定义在主函数后面也不会出错
 * 如果要将函数赋值给函数指针, 需要提前声明这些函数
int get_line(char *, int);
int read_lines(char **, int);
void write_lines(char **, int);
void q_sort(void *v[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int main(int argc, char **argv)
        char line[MAXLEN];





    
        int len, nlines, numberic = 0;
        p_read_lines = read_lines;
        p_write_lines = write_lines;
        p_q_sort = q_sort;
        //如果参数中含有 -n 说明这是按照数值顺序排序, 否则就按照字典顺序排序
        if(argc > 1 && strcmp(argv[1], "-n") == 0)
                numberic = 1;
        if((nlines = (*p_read_lines)(lineptr, MAXLINES)) >= 0)
                 * 注意 : 
                 *   使用 ? : 表达式选择 函数指针, 函数指针类型转换的时候, 为每个选项都添加转换
                 *   如果只转换 ? : 结果, 会报出警告
                (*p_q_sort)((void **)lineptr, 0, nlines - 1, numberic ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp);
                (*p_write_lines)(lineptr, nlines);
                return 0;
                printf("error \n");
                return 1;
//从标准输入流中读取数据, 放入字符串中
int get_line(char *line, int max)
        int i, c;
        for(i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i ++)
                *(line + i) = c;
        *(line + i) = '\0';
        return i;
//从标准输入流中读取数据, 放入字符串数组
int read_lines(char *lineptr[], int max)
        int len, nlines = 0;
        char *p, line[MAXLEN];
        while((len = get_line(line, MAXLEN)) > 0)
                if(nlines >= MAXLINES || (p = malloc(sizeof(char) * (len + 1))) == NULL)
                        return -1;





    
                        strcpy(p, line);
                        *(lineptr + nlines) = p;
                        nlines++;
        return nlines;
//将字符串数组中的元素打印出来
void write_lines(char *lineptr[], int nlines)
        int i;
        for(i = 0; i < nlines; i++)
                printf("the %d char sequence is : %s \n", i, *(lineptr + i));
//数值比较
int numcmp(char *s1, char *s2)
        double v1, v2;
        v1 = atof(s1);
        v2 = atof(s2);
        if(v1 < v2)
                return -1;
        else if(v1 > v2)
                return 1;
        else if(v1 == v2)
                return 0;
//交换数组中 i j 元素
void swap(void *v[], int i, int j)
        void *temp;
        temp = *(v + i);
        *(v + i) = *(v + j);
        *(v + j) = temp;
//排序方法
void q_sort(void *v[], int left, int right, int (*comp)(void *, void *))
        int i, last;
        if(left >= right)





    
                return;
        swap(v, left, (left + right)/2);
        last = left;
        for(i = last + 1; i <= right; i++)
                if((*comp)(v[i], v[left]) < 0)
                        swap(v, ++last, i);
        swap(v, left, last);
        q_sort(v, left, last - 1, comp);
        q_sort(v, last + 1, right, comp);
}



.


3. 指针函数的复杂案例分析



(1) 指针函数 和 函数指针 分析



示例 :


-- 示例一 : char *get_line(char *line, int max);


-- 示例二 : char **get_line(char *line, int max);


-- 示例三 : int *(*get_line)(char *line, int max);




分析 :


-- 示例一 : get_line 普通函数, 返回值是一个char类型指针, 即返回一个字符串;


-- 示例二 : get_line 普通函数, 返回值是一个二级指针, 即字符串数组;


-- 示例三 : get_line 函数指针, 该指针的返回值是一个int类型的指针, 即指针; get_line不是函数名, 是一个指针变量, 使用 int *(*)(char *line, int max) get_line 可以清楚的定义该指针, 不过如果这样定义就错误了;




(2) 指函数指针转换



示例 :



char fun();
void (*p)();
*(char*)&p = (char)fun;
(*p)();



解析 :

-- void (*p)() : 该表达式定义了一个函数指针, 该指针p 指向一个函数, 这个函数的返回值 和 参数都为NULL;


-- *(char*)&p : p是函数指针, &p 是指向函数指针的指针, (char*)&p 将 指向函数指针的指针 类型改为 char*, *(char)&p 就是 取出转换类型的函数指针, 这个是已经转换好类型的函数指针;


-- (char)fun : 将fun函数的 函数指针转换为 char 类型, 函数的入口地址转换为 char 类型;


-- *(char*)&p = (char)fun : 指的是将函数的地址 赋值给 指针变量p;


-- (*p)() : 调用这个 指针 指向的函数;




(3) 将地址转换成函数指针



示例 :



(*(void(*)())0)() ;


解析 :


-- void(*)() : 函数指针类型, 该类型指向的函数 返回值 和 参数 均为 NULL;


-- (void(*)())0 : 将 0 转换为函数指针, 前提是这个位置有该类型的函数;


-- *(void(*)())0 : 将 函数指针 指向的函数取出, 后面加上(), 就是执行这个函数;





(4) 函数指针作为返回值



示例 : 函数指针作为返回值, 正向写写不出来, 要反向推理;



char(*get_char(char))(char *, int);




分析 : 从get_char 开始解析;

-- get_char(char) : get_char 先跟()结合, 表明这是一个函数;


-- *get_char(char) : get_char(char) 与 * 结合, 表明该函数返回值是一个指针;


-- (*get_char(char))(char *, int) : (*get_char(char)) 返回指针, 与后面的(char *, int)结合, 返回的是一个函数指针, 该指针指向函数;


-- char(*get_char(char))(char*, int) : 表明这个返回值函数指针指向的函数的返回值是 char 类型;




简单的替代方法 : 使用 typedef;



typedef char(*RETURN)(char *, int);
the 2 char sequence is : r 
the 3 char sequence is : w 
61                      return 0;
(gdb) n
69      }
(gdb) n
0x000000306801d994 in __libc_start_main () from /lib64/libc.so.6
(gdb) quit #退出调试
A debugging session is active.
        Inferior 1 [process 7799] will be killed.
Quit anyway? (y or n) y
【Groovy】xml 序列化 ( 使用 StreamingMarkupBuilder 生成 xml 数据 | mkp.xmlDeclaration() 生成 xml 版本数据 ) 【Groovy】xml 序列化 ( 使用 MarkupBuilder 生成 xml 数据 | 标签闭包下创建子标签 | 使用 MarkupBuilderHelper 添加 xml 注释 ) 【Groovy】xml 序列化 ( 使用 MarkupBuilder 生成 xml 数据 | 设置 xml 标签内容 | 设置 xml 标签属性 ) 【Groovy】json 字符串反序列化 ( 使用 JsonSlurper 进行 json 字符串反序列化 | 根据 map 集合构造相关类 ) 【Groovy】json 序列化 ( 类对象转为 json 字符串 | 使用 JsonBuilder 进行转换 | 使用 JsonOutput 进行转换 | 将 json 字符串格式化输出 ) 这是我看过最全面讲解嵌入式C语言回调函数和函数指针的教程
前面分享了关于指针和结构体使用过程,今天是同系列的函数指针和回调函数。函数指针是指向函数的指针变量。通过函数指针C语言可以实现各种强大的功能与设计方法。而回调函数是函数指针最常见的用途,是C语言的重中之重,也是C语言面试当中的必考知识点和难点。
【Groovy】xml 序列化 ( 使用 StreamingMarkupBuilder 生成 xml 数据 | mkp.xmlDeclaration() 生成 xml 版本数据 ) 【Groovy】xml 序列化 ( 使用 MarkupBuilder 生成 xml 数据 | 标签闭包下创建子标签 | 使用 MarkupBuilderHelper 添加 xml 注释 ) 【Groovy】xml 序列化 ( 使用 MarkupBuilder 生成 xml 数据 | 设置 xml 标签内容 | 设置 xml 标签属性 ) 【Groovy】json 字符串反序列化 ( 使用 JsonSlurper 进行 json 字符串反序列化 | 根据 map 集合构造相关类 ) 【Groovy】json 序列化 ( 类对象转为 json 字符串 | 使用 JsonBuilder 进行转换 | 使用 JsonOutput 进行转换 | 将 json 字符串格式化输出 )