一般情况下,在C语言中,函数指针定义时就会说明其指向的函数的参数情况以及返回值类型,比如以下定义:
void (*func_p)(int a);
以上代码就声明了一个函数指针func_p,其指向的函数的返回值类型为void类型,即没有返回值,并且该函数有且只有一个int类型的参数。
那有没有方法使得函数指针能够指向参数类型不同的函数呢?事实上,在gcc中,我们可以使用透明联合类型。这里所谓的“透明”,指的是对用户而言,该联合类型是透明的。自然,用户也就无法发觉。
透明联合类型的定义的一个范例如下所示:
typedef union u_t { int *a; float b; struct { short c; char d; }; } u_t __attribute__((__transparent_union__));
从中可以看出,透明联合类型定义时,同普通的联合类型是很相似的,只是要在末尾加上attribute属性transparent_union。事实上__transparent_union__的确也可以写为transparent_union,但为了避免与自定义的宏重复,最好在两边加上双下划线。
另外,对透明联合类型的成员有以下两点要求:
- 浮点类型(float, double, float _Complex, 以及double _Complex)以及向量类型(vector)可以作为透 明联合类型的成员,但是不能作为第一个成员。
- 透明联合类型中的任意一个成员的所占的内存空间的大小必须小于等于该透明联合类型中的第一个成员的所占的内存空间的大小。
利用该透明联合类型,我们可以定义如下的函数指针:
void (*func_p)(u_t u);
该函数指针可以正确无误的指向以下两个函数(所谓正确无误是指不会提示assignment from incompatible pointer type):
void func_a(int *a) { /*Some code here*/ } void func_b(float b) { /*Some code here*/ }
由于u_t中第三个参数为匿名结构体,所以无法写出一个参数为该结构体的函数,函数指针自然也无法指向它。
我们需要注意,下面一段代码也是可以顺利编译通过的:
func_p=func_a; func_p(4.5F);
而下面一个语句是无法编译通过的:
func_a(4.5F);
从中也可以看出,透明联合类型削弱了C语言的类型检测机制。或者,换言之,它起到了类似强制类型转换的效果。GObject在定义_G_DEFINE_BOXED_TYPE_BEGIN宏时,便利用了这一点:
#define _G_DEFINE_BOXED_TYPE_BEGIN(TypeName, type_name, copy_func, free_func) \ GType \ type_name##_get_type (void) \ { \ static volatile gsize g_define_type_id__volatile = 0; \ if (g_once_init_enter (&g_define_type_id__volatile)) \ { \ GType (* _g_register_boxed) \ (const gchar *, \ union \ { \ TypeName * (*do_copy_type) (TypeName *); \ TypeName * (*do_const_copy_type) (const TypeName *); \ GBoxedCopyFunc do_copy_boxed; \ } __attribute__((__transparent_union__)), \ union \ { \ void (* do_free_type) (TypeName *); \ GBoxedFreeFunc do_free_boxed; \ } __attribute__((__transparent_union__)) \ ) = g_boxed_type_register_static; \ GType g_define_type_id = \ _g_register_boxed (g_intern_static_string (#TypeName), copy_func, free_func); \ { /* custom code follows */
通过_g_register_boxed这一函数指针,copy_func和free_func的参数类型得以放宽,增加了用户的自由度。
事实上,透明联合类型也可以用在函数中,作为函数的参数或返回值的类型来使用。
例如以下一个函数:
u_t func_u(u_t u); { /*Some code here*/ }
该函数调用时,其实参的类型可以是int类型,也可以是float类型,同时,其返回值类型也是不确定的。换言之,通过透明联合类型,我们使得一个函数可以接受多种类型的参数,返回多种类型的参数。
考虑到在底层,类型实质上是不存在的,因此所谓的透明联合类型,也就是在一定程度上打破了类型对我们的束缚,使数据以一种更底层的角度呈现在我们面前。不过这样也弱化了C语言对类型的检测,由此也可能带来一些很严重的错误。