空指针与函数指针

空指针 void pointers

指针void 是一种特殊类型的指针。void 指针可以指向任意类型的数据,可以是整数,浮点数甚至字符串。唯一个限制是被指向的数值不可以被直接引用(不可以对它们使用引用星号*),因为它的长度是不定的,因此,必须使用类型转换操作或赋值操作来把void 指针指向一个具体的数据类型。

它的应用之一是被用来给函数传递通用参数

// integer increaser
#include <iostream.h>

void increase (void* data, int type) {
    switch (type) {
        case sizeof(char) : (*((char*)data))++; break;
        case sizeof(short): (*((short*)data))++; break;
        case sizeof(long) : (*((long*)data))++; break;
    }
}

int main () {
    char a = 5;
    short b = 9;
    long c = 12;
    increase (&a,sizeof(a));
    increase (&b,sizeof(b));
    increase (&c,sizeof(c));
    cout << (int) a << ", " << b << ", " << c;
    return 0;
}
  • 6, 10, 13

sizeof 是C++的一个操作符,用来返回其参数的长度字节数常量。例如,sizeof(char) 返回 1,因为 char 类型是1字节长数据类型。

函数指针 Pointers to functions

注:函数指针有两个用途:调用函数和做函数的参数。

注:函数名也是指向函数第一条指令的常量指针。

C++ 允许对指向函数的指针进行操作。它最大的作用是把一个函数作为参数传递给另外一个函数。声明一个函数指针像声明一个函数原型一样,除了函数的名字需要被括在括号内并在前面加星号asterisk (*)。例如:

// pointer to functions
#include <iostream.h>

int addition (int a, int b) {
    return (a+b);
}

int subtraction (int a, int b) {
    return (a-b);
}

int (*minus)(int, int) = subtraction;

int operation (int x, int y, int (*functocall)(int,int)) {
    int g;
    g = (*functocall)(x,y);
    return (g);
}

int main () {
    int m,n;
    m = operation (7, 5, addition);
    n = operation (20, m, minus);
    cout <<n;
    return 0;
}   
  • 8

在这个例子里, minus 是一个全局指针,指向一个有两个整型参数的函数,它被赋值指向函数subtraction,所有这些由一行代码实现:

int (* minus)(int, int) = subtraction;

这里似乎解释的不太清楚,有问题问为什么(int int)只有类型,没有参数,就再多说两句。

这里 int (*minus)(int int)实际是在定义一个指针变量,这个指针的名字叫做minus,这个指针的类型是指向一个函数,函数的类型是有两个整型参数并返回一个整型值。

整句话“int (*minus)(int,int) = subtraction;”是定义了这样一个指针并把函数subtraction的值赋给它,也就是说有了这个定义后minus就代表了函数subtraction。因此括号中的两个int int实际只是一种变量类型的声明,也就是说是一种形式参数而不是实际参数。

项目中用到了很多封装在struct中的函数指针,以前在MFC里面经常用到则个作为回调函数,还以为是微软设计的特色呢。在网上查了一下它的用法,做个总结。

1)提供调用的灵活性。设计好了一个函数框架,但是设计初期并不知道自己的函数会被如何使用。比如C的”stdlib”中声明的qsort函数,用来对数值进行排序。显然,顺序还是降序,元素谁大谁小这些问题,库程序员在编写qsort的时候不可能决定。这些问题是要在用户调用这个函数的时候才能够决定。那边qsort如何保证通用性和灵活性呢?采用的办法是让函数的使用者来制定排序规则。于是调用者应该自己设计comparator函数,传给qsort函数。这就在程序设计初期保证了灵活性。尽管使用函数指针使得程序有些难懂,但是这样的牺牲还是值得的。

2)提供封装性能。有点面向对象编程的特点。比如设计一个

typedef struct _c_stack{ 
    int base_size; 
    int point; 
    int * base; 
    int size; 
    int (*pop)(struct _c_stack *); 
    int (*push)(int,struct _c_stack *); 
    int (*get_top)(struct _c_stack); 
} c_stack;

化完之后,用户调用这个结构体上的pop函数,只需要s.pop(&s)即可。即使这个时候,工程内部有另外一个函数名字也叫pop,他们之间是不会发生名字上的冲突的。

原因很简单,因为结构体中的函数指针指向的函数名字可能是

int ugly_stupid_no_one_will_use_this_name_pop(c_stack *)

,只是stack的用户是不知道他在调用s.pop(&s),实际上起作用的是这样一个有着冗长名字的函数。
函数指针这种避免命名冲突上的额外好处对于一些库函数的编写者是很有意义的,因为库可能被很多的用户在许多不同的环境下使用,这样就能有效的避免冲突而保证库的可用性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值