用c语言实现函数重载 您所在的位置:网站首页 c中对重载函数的调用不明确怎么办 用c语言实现函数重载

用c语言实现函数重载

2024-07-16 21:31| 来源: 网络整理| 查看: 265

一.    什么是函数重载?        函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表(参数个数、类型、顺序)的函数,这组函数被称为重载函数。重载函数通常用来声明一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

二、为什么要用函数重载在我们之前学习的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 实验室设备网 版权所有