C语言实现函数重载 您所在的位置:网站首页 c语言操作符重载 C语言实现函数重载

C语言实现函数重载

2024-01-04 22:08| 来源: 网络整理| 查看: 265

C语言实现函数重载

文章目录 C语言实现函数重载一、C++是如何实现函数重载的二、C++函数的命名规则三、C语言实现函数重载1.可变参数2.函数指针实现参数的重载3.实现参数类型的重载4.完整版

一、C++是如何实现函数重载的 C++ 实现函数重载很大程度上依赖与编译器对函数名的 Mangling(损坏,破坏),即 C++ 的源代码被编译后同名的重载函数名字会被破坏,一般是在原函数名前后加上特定的字符串(g++编译器中通过在函数名后面添加参数的后缀),以区分不同重载函数,然后在调用的时候根据参数的不同选择合适的函数

如下代码说明了编译器是如何处理普通函数重载的

几个同名的重载函数仍然是不同的函数,编译后应该有不同的地址,那么它们是如何区分的呢?我们自然想到函数接口的两个要素:参数与返回值。 如果同名函数的参数不同(包括类型、顺序不同),那么容易区别出它们是不同的函数。如果同名函数仅仅是返回值类型不同,有时可以区分,有时却不能。例如: void Function(void); int Function (void); 上述两个函数,第一个没有返回值,第二个的返回值是 int 类型。如果这样调用函数: int x = Function (); 则可以判断出 Function 是第二个函数。问题是在 C/C++程序中,我们可以忽略函数的返回值。在这种情况下,编译器和程序员都不知道哪个 Function 函数被调用。 所以只能靠参数而不能靠返回值类型的不同来区分重载函数。 编译器根据参数为每个重载函数产生不同的内部标识符。不同的编译器可能产生不同风格的内部标识符 如果 C++程序要调用已经被编译后的 C 函数,该怎么办?

假设某个 C 函数的声明如下:

void foo(int x, int y);

该函数被 C 编译器编译后在库中的名字为_foo,而 C++编译器则会产生像_foo_int_int之类的名字用来支持函数重载和类型安全连接。

这样一来很明显,C和C++中对函数的生成规则是不同的,由于编译后的名字不同,C++程序不能直接调用 C 函数。

但是C++是完全兼容C的,而且我们的C++程序往往在不断的调用C库,C++提供了一个 C 连接交换指定符号 extern“C”来解决这个问题。

extern “C” { void foo(int x, int y); … // 其它函数 } 或者写成 extern “C” { #include “myheader.h” … // 其它 C 头文件 }

这就告诉 C++编译译器,函数 foo 是个C库的函数,那么C++编译器应该按照C编译器的编译和链接规则来进行链接,也就是说到库中找名字_foo 而不是找_foo_int_int。

C++编译器开发商已经对 C 标准库的头文件作了 extern“C”处理,所以我们可以用#include 直接引用这些头文件。

二、C++函数的命名规则 #include using namespace std; int func(void) { cout cout va_list va; va_start(va,n); // init va, pointing to the first argument int smax = -1; for(int i = 0;i printf("%d\n",getMax(4,9,5,2,19)); printf("%d\n",getMax(6,1,3,4,5,2,0)); } 参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈 因此,假设定义一个可变参数的函数 void f(int x, …), 通过f( x, y, z) 调用,那么,z先入栈,然后y, 然后x。 因此我们只要得到任何一个变量的地址,就可以找到其它变量的地址。 va_start(va, n) 就是让va指向n的地址。这样,后面就可以得到所有参数的值。前提是,我们必须知道每个参数的类型。在本例子中,都是int类型。 2.函数指针实现参数的重载 #include #include typedef struct _int_param { int param1; int param2; }INT_PARAM; typedef struct _double_param_ { double param1; double param2; }DOUBLE_PARAM; typedef void* (*ADDFUNC)(void*); void* int_add_func(void* wParam) { INT_PARAM* lParam = (INT_PARAM*)wParam; int res = lParam->param1 + lParam->param2; printf("result = %d\n", res); } void* double_add_func(void* wParam) { DOUBLE_PARAM* lParam = (DOUBLE_PARAM*)wParam; double res = lParam->param1 + lParam->param2; printf("result = %f\n", res); } void* add_func(ADDFUNC f, void* wParam) { return f(wParam); } int main() { INT_PARAM val1 = {10, 20}; DOUBLE_PARAM val2 = {30.5, 40.5}; add_func(int_add_func, &val1); add_func(double_add_func, &val2); return 0; } 3.实现参数类型的重载

这主要是利用了 GCC 的内置函数,__builtin_types_compatible_p()和__builtin_choose_expr(),

例如:

struct s1 { int a; int b; double c; }; struct s2 { long long a; long long b; }; void gcc_overload_s1(struct s1 s) { printf("Got a struct s1: %d %d %f\n", s.a, s.b, s.c); } void gcc_overload_s2(struct s2 s) { printf("Got a struct s2: %lld %lld\n", s.a, s.b); } // warning: dereferencing type-punned pointer will break strict-aliasing rules #define gcc_overload(A)\ __builtin_choose_expr(__builtin_types_compatible_p(typeof(A), struct s1),\ gcc_overload_s1(*(struct s1 *)&A),\ __builtin_choose_expr(__builtin_types_compatible_p(typeof(A), struct s2),\ gcc_overload_s2(*(struct s2 *)&A),(void)0))

或者一个更高级的写法

void gcc_type_overload_aux(int typeval, ...) { switch(typeval) { case 1: { va_list v; va_start(v, typeval); struct s1 s = va_arg(v, struct s1); va_end(v); gcc_overload_s1(s); break; } case 2: { va_list v; va_start(v, typeval); struct s2 s = va_arg(v, struct s2); va_end(v); gcc_overload_s2(s); break; } default: { printf("Invalid type to 'gcc_type_overload()'\n"); exit(1); } } } #define gcc_type_overload(A)\ gcc_type_overload_aux(\ __builtin_types_compatible_p(typeof(A), struct s1) * 1\ + __builtin_types_compatible_p(typeof(A), struct s2) * 2\ , A) 4.完整版 #include #include #include void va_overload2(int p1, int p2) { printf("va_overload2 %d %d\n", p1, p2); } void va_overload3(int p1, int p2, int p3) { printf("va_overload3 %d %d %d\n", p1, p2, p3); } static void va_overload(int p1, int p2, ...) { if (p2 == 7) { va_list v; va_start(v, p2); int p3 = va_arg(v, int); va_end(v); va_overload3(p1, p2, p3); return; } va_overload2(p1, p2); } static void print_nt_strings(const char *s, ...) { va_list v; va_start(v, s); /* Stop on NULL */ while (s) { printf("%s", s); /* Grab next parameter */ s = va_arg(v, const char *); } va_end(v); } #define COUNT_PARMS2(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _, ...) _ #define COUNT_PARMS(...)\ COUNT_PARMS2(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) static void print_strings2(int count, ...) { int i; va_list v; va_start(v, count); for (i = 0; i printf("One param: %d\n", p1); } void count_overload2(double *p1, const char *p2) { printf("Two params: %p (%f) %s\n", p1, *p1, p2); } void count_overload3(int p1, int p2, int p3) { printf("Three params: %c %d %d\n", p1, p2, p3); } void count_overload_aux(int count, ...) { va_list v; va_start(v, count); switch(count) { case 1: { int p1 = va_arg(v, int); count_overload1(p1); break; } case 2: { double *p1 = va_arg(v, double *); const char *p2 = va_arg(v, const char *); count_overload2(p1, p2); break; } case 3: { int p1 = va_arg(v, int); int p2 = va_arg(v, int); int p3 = va_arg(v, int); count_overload3(p1, p2, p3); break; } default: { va_end(v); printf("Invalid arguments to function 'count_overload()'"); exit(1); } } va_end(v); } #define count_overload(...)\ count_overload_aux(COUNT_PARMS(__VA_ARGS__), __VA_ARGS__) void cpp_overload1(int p1) { printf("CPP One param: %d\n", p1); } void cpp_overload2(double *p1, const char *p2) { printf("CPP Two params: %p (%f) %s\n", p1, *p1, p2); } void cpp_overload3(int p1, int p2, int p3) { printf("CPP Three params: %c %d %d\n", p1, p2, p3); } #define CAT(A, B) CAT2(A, B) #define CAT2(A, B) A ## B #define cpp_overload(...)\ CAT(cpp_overload, COUNT_PARMS(__VA_ARGS__))(__VA_ARGS__) #define cpp_default1(A) cpp_default2(A, "default string") void cpp_default2(int x, const char *s) { printf("Got %d %s\n", x, s); } #define cpp_default(...)\ CAT(cpp_default, COUNT_PARMS(__VA_ARGS__))(__VA_ARGS__) void sizeof_overload_float(float f) { printf("Got float %f\n", f); } void sizeof_overload_double(double d) { printf("Got double %f\n", d); } void sizeof_overload_longdouble(long double ld) { printf("Got long double %Lf\n", ld); } #define sizeof_overload(A)\ ((sizeof(A) == sizeof(float))?sizeof_overload_float(A):\ (sizeof(A) == sizeof(double))?sizeof_overload_double(A):\ (sizeof(A) == sizeof(long double))?sizeof_overload_longdouble(A):(void)0) struct s1 { int a; int b; double c; }; struct s2 { long long a; long long b; }; void gcc_overload_s1(struct s1 s) { printf("Got a struct s1: %d %d %f\n", s.a, s.b, s.c); } void gcc_overload_s2(struct s2 s) { printf("Got a struct s2: %lld %lld\n", s.a, s.b); } // warning: dereferencing type-punned pointer will break strict-aliasing rules #define gcc_overload(A)\ __builtin_choose_expr(__builtin_types_compatible_p(typeof(A), struct s1),\ gcc_overload_s1(*(struct s1 *)&A),\ __builtin_choose_expr(__builtin_types_compatible_p(typeof(A), struct s2),\ gcc_overload_s2(*(struct s2 *)&A),(void)0)) void gcc_type_overload_aux(int typeval, ...) { switch(typeval) { case 1: { va_list v; va_start(v, typeval); struct s1 s = va_arg(v, struct s1); va_end(v); gcc_overload_s1(s); break; } case 2: { va_list v; va_start(v, typeval); struct s2 s = va_arg(v, struct s2); va_end(v); gcc_overload_s2(s); break; } default: { printf("Invalid type to 'gcc_type_overload()'\n"); exit(1); } } } #define gcc_type_overload(A)\ gcc_type_overload_aux(\ __builtin_types_compatible_p(typeof(A), struct s1) * 1\ + __builtin_types_compatible_p(typeof(A), struct s2) * 2\ , A) void print_type(int t, va_list *v) { switch(t) { case 1: { int p = va_arg(*v, int); printf("int :%d\n", p); break; } case 2: { long long p = va_arg(*v, long long); printf("long long :%lld\n", p); break; } case 3: { double p = va_arg(*v, double); printf("double :%f\n", p); break; } case 4: { long double p = va_arg(*v, long double); printf("long double :%Lf\n", p); break; } default: { printf("Unknown type\n"); exit(1); } } } void param_lister1_aux(int t1, ...) { va_list v; va_start(v, t1); printf("1st param:"); print_type(t1, &v); va_end(v); } void param_lister2_aux(int t1, ...) { int t2; va_list v; va_start(v, t1); printf("1st param:"); print_type(t1, &v); t2 = va_arg(v, int); printf("2nd param:"); print_type(t2, &v); va_end(v); } void param_lister3_aux(int t1, ...) { int t2, t3; va_list v; va_start(v, t1); printf("1st param:"); print_type(t1, &v); t2 = va_arg(v, int); printf("2nd param:"); print_type(t2, &v); t3 = va_arg(v, int); printf("3rd param:"); print_type(t3, &v); va_end(v); } void param_lister4_aux(int t1, ...) { int t2, t3, t4; va_list v; va_start(v, t1); printf("1st param:"); print_type(t1, &v); t2 = va_arg(v, int); printf("2nd param:"); print_type(t2, &v); t3 = va_arg(v, int); printf("3rd param:"); print_type(t3, &v); t4 = va_arg(v, int); printf("4th param:"); print_type(t4, &v); va_end(v); } #define TYPENUM(A)\ __builtin_types_compatible_p(typeof(A), int) * 1\ + __builtin_types_compatible_p(typeof(A), long long) * 2\ + __builtin_types_compatible_p(typeof(A), double) * 3\ + __builtin_types_compatible_p(typeof(A), long double) * 4 #define param_lister1(A)\ param_lister1_aux(TYPENUM(A), A) #define param_lister2(A, B)\ param_lister2_aux(TYPENUM(A), A, TYPENUM(B), B) #define param_lister3(A, B, C)\ param_lister3_aux(TYPENUM(A), A, TYPENUM(B), B, TYPENUM(C), C) #define param_lister4(A, B, C, D)\ param_lister4_aux(TYPENUM(A), A, TYPENUM(B), B, TYPENUM(C), C, TYPENUM(D), D) #define param_lister(...)\ CAT(param_lister, COUNT_PARMS(__VA_ARGS__))(__VA_ARGS__) int main() { param_lister(1); param_lister(1, 2.0, 3, 6.0); return 0; }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有