用c语言实现函数重载 | 您所在的位置:网站首页 › c中对重载函数的调用不明确怎么办 › 用c语言实现函数重载 |
一. 什么是函数重载? 函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表(参数个数、类型、顺序)的函数,这组函数被称为重载函数。重载函数通常用来声明一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。 二、为什么要用函数重载在我们之前学习的C中,我们对一个功能函数要实现不同类型的调用时,就必须得取不同的名称。如果调用的非常的多,就必须得起好多的名字,这样就大大增加了工作量,所以在C++中,我们就考虑到了函数重载。 三、 C++函数重载如何实现? 在C++的底层,有重命名机制,比如下面这个函数。 实现一个加法函数,可以对int型、double型、long型进行加法运算。在C++中,我们可以这样做: #includeusing namespace std;int Add(int left, int right){ return left + right;} double Add(double left, double right){ return left + right;} long Add(long left, long right){ return left + right;} int main(){ Add(10, 10); Add(10.0, 10.0); Add(10L, 10L); return 0;} 通过上面代码的实现,可以根据具体的Add()的参数去调用对应的函数。 底层的重命名机制将Add函数根据参数的个数,参数的类型,返回值的类型都做了重新命名。那么借助函数重载,一个函数就有多种命名机制。 在C++调用约定(_cdecl 调用约定)中Add函数在底层被解析为:
"int __cdecl Add(int,int)" (?Add@@YAHHH@Z)"double __cdecl Add(double,double)" (?Add@@YANNN@Z)"long __cdecl Add(long,long)" (?Add@@YAJJJ@Z) 在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明? (1)C++中可以通过在函数声明前加 extern "C" 将一个函数按照 C 语言的风格来进行编译。(2)C++语言支持函数重载。而C不支持函数重载。 (3)函数在C中和C++中编译过的函数名字是不一样的。加上extern”C”是说明是说明C已经编译过的。 C++想要调用已经编译过的C函数,由于编译过的名字不同,是不能直接调用的,所以C++加extern“C”生命来解决这个问题。 例如:假设某个函数的原型为: void foo(int x, int y);该函数被C 编译器编译后在库中的名字为_foo, 而C++ 编译器则会产生像 "int __cdecl Add(int,int)" (?Add@@YAHHH@Z)"double __cdecl Add(double,double)" (?Add@@YANNN@Z)"long __cdecl Add(long,long)" (?Add@@YAJJJ@Z)_foo_int_int 之类的名字,加上extren”C”后,就相当于告诉编译器,函数foo是个C编译后的函数,在库里应该找的是_foo,而不是_foo_int_int. 。
接下来讲讲怎么通过c语言去实现函数重载 C语言实现函数重载(1)利用可变参数但是,在很多情况下,利用可变参数可以实现 C 语言的函数重载的,POSIX 接口中定义的 open 函数就是一个非常好的例子, #include #include #include int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode); 以下是一个简单的例子,”重载”了两个函数,第一个函数是两个参数,第二个函数带了三个函数,其中第三个函数是可选的, ANSI C 标准中,有可变参数的概念,可以通过一组宏实现 函数 描述 col 3 is right-alignedva_list arg_ptr 定义一个可变参数列表指针va_start(arg_ptr, argN) 让arg_ptr指向参数argNva_arg(arg_ptr, type) 返回类型为type的参数指针,并指向下一个参数va_copy(dest, src) 拷贝参数列表指针,src->dest,va_end(arg_ptr) 清空参数列表,并置参数指针arg_ptr无效。每个va_start()必须与一个va_end()对应 #include #include int getMax(int n, ...) { va_list va; va_start(va,n); // init va, pointing to the first argument int tmp,smax=-1; int i; for(i=0;ismax) smax=tmp; } va_end(va); return smax; } int main() { 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类型。函数指针实现的参数重载(这个是重点,要掌握) #include void func_int(void * a){ printf("%d\n",*(int*)a); //输出int类型,注意 void * 转化为int} void func_double(void * b){ printf("%.2f\n",*(double*)b);} typedef void (*ptr)(void *); //typedef申明一个函数指针 void c_func(ptr p,void *param){ p(param); //调用对应函数} int main(){ int a = 23; double b = 23.23; c_func(func_int,&a); c_func(func_double,&b); 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) 转自:https://blog.csdn.net/ypshowm/java/article/details/89096042 |
CopyRight 2018-2019 实验室设备网 版权所有 |