用c语言实现函数重载

发布时间:2024-07-25  

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


二、为什么要用函数重载
在我们之前学习的C中,我们对一个功能函数要实现不同类型的调用时,就必须得取不同的名称。如果调用的非常的多,就必须得起好多的名字,这样就大大增加了工作量,所以在C++中,我们就考虑到了函数重载。

三、 C++函数重载如何实现?
    在C++的底层,有重命名机制,比如下面这个函数。

    实现一个加法函数,可以对int型、double型、long型进行加法运算。在C++中,我们可以这样做:

#include
using 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-aligned
va_list arg_ptr    定义一个可变参数列表指针
va_start(arg_ptr, argN)    让arg_ptr指向参数argN
va_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;i        {  
                tmp=va_arg(va,int); // get the next argument, the type is int  
                if(tmp>smax) 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("%dn",*(int*)a); //输出int类型,注意 void * 转化为int
}

void func_double(void * b)
{
printf("%.2fn",*(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 %fn", s.a, s.b, s.c);
}

void gcc_overload_s2(struct s2 s)
{
    printf("Got a struct s2: %lld %lldn", 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)


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>