C语言实现函数重载

函数重载



大家都知道 C++ 等支持面向对象的语言支持函数重载,那么编译器是如何辨别这些函数的呢?

CPP如何实现重载



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

几个同名的重载函数仍然是不同的函数,编译后应该有不同的地址,那么它们是如何区分的呢?我们自然想到函数接口的两个要素: 参数与返回值。
如果同名函数的参数不同(包括类型、顺序不同),那么容易区别出它们是不同的函数。如果同名函数仅仅是返回值类型不同,有时可以区分,有时却不能。例如:

void Function(void);
int Function (void);
  • 1
  • 2
  • 1
  • 2

上述两个函数,第一个没有返回值,第二个的返回值是 int 类型。如果这样调用函数:

int x = Function ();
  • 1
  • 1

则可以判断出 Function 是第二个函数。问题是在 C++/C 程序中,我们可以忽略函数的返回值。在这种情况下,编译器和程序员都不知道哪个 Function 函数被调用。

所以只能靠参数而不能靠返回值类型的不同来区分重载函数

编译器根据参数为每个重载函数产生不同的内部标识符。不同的编译器可能产生不同风格的内部标识符

如果 C++程序要调用已经被编译后的 C 函数,该怎么办?
假设某个 C 函数的声明如下:

void foo(int x, int y);
  • 1
  • 1

该函数被 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 头文件
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

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

CPP函数的命名规则



比如如下的C++代码

#include <iostream>
using namespace std;

int func(void)
{
    cout << "func without parameters" << endl;
}

int func(int ia)
{
    cout << "func with one int parameter: " << endl;
    cout << ia << endl;
}

int func(int ia, float fb)
{
    cout << "func with one int parameter and one float parameter" << endl;
    cout << ia << endl;
    cout << fb << endl;
}

int main()
{

    func();
    func(5);
    func(5, 5.0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

我们可以通过g++的-S指令,将我们的程序编译成汇编

main:
.LFB1052:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $32, %rsp
    .seh_stackalloc 32
    .seh_endprologue
    call    __main
    call    _Z4funcv
    movl    $5, %ecx
    call    _Z4funci
    movss   .LC3(%rip), %xmm1
    movl    $5, %ecx
    call    _Z4funcif
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .def    __tcf_0;    .scl    3;  .type   32; .endef
    .seh_proc   __tcf_0
__tcf_0:
.LFB1063:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

可以看到,func 的三个版本重载函数在编译后名字都被破坏了,编译器将他们重命名为了 _Z4funcv, _Z4funci, _Z4funcif, (g++ 编译器可能根据函数参数类型为函数名加上了与参数类型相关的特定后缀,如func(void) 变成了 _Z4funcv, func(int) 变成了_Z4funci, func(int, float)变成了 _Z4funcif),然后在调用各个版本的func()时,编译器根据参数类型的不同选择合适的重载函数,如调用 func() 其实是调用了 _Z4funcv, 调用 func(5, 5.0)实际上是调用了 _Z4funcif等。

但是,在很多情况下,利用可变参数可以实现 C 语言的函数重载的,POSIX 接口中定义的 open 函数就是一个非常好的例子,

C语言的函数不支持重载


#include <stdio.h>

int func(void)
{
    printf("func in C...\n");
}

int main()
{

    func();

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

编译成汇编可以看到

main:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $32, %rsp
    .seh_stackalloc 32
    .seh_endprologue
    call    __main
    call    func
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-1) 5.1.0"
    .def    puts;   .scl    2;  .type   32; .endef

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

编译器处理后,函数命名仍然是func,没有加上对参数的识别。

C语言实现函数重载


可变参数



但是,在很多情况下,利用可变参数可以实现 C 语言的函数重载的,POSIX 接口中定义的 open 函数就是一个非常好的例子,

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

以下是一个简单的例子,”重载”了两个函数,第一个函数是两个参数,第二个函数带了三个函数,其中第三个函数是可选的,

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<stdio.h>
#include<stdarg.h>
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<n;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));
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈
因此,假设定义一个可变参数的函数 void f(int x, …), 通过f( x, y, z) 调用,那么,z先入栈,然后y, 然后x。 因此我们只要得到任何一个变量的地址,就可以找到其它变量的地址。
va_start(va, n) 就是让va指向n的地址。这样,后面就可以得到所有参数的值。前提是,我们必须知道每个参数的类型。在本例子中,都是int类型。

函数指针实现的参数重载


#include <stdio.h>
#include <stdlib.h>

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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

实现参数类型的重载



这主要是利用了 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))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

或者一个更高级的写法:

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

另外两种用 C 实现函数重载的方法可以是利用宏和预处理,以及函数指针,只不过具体的重载方式也要根据特定的应用场景来决定。

最终版


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

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 < count; i++)
    {
        /* Grab next parameter + print it */
        const char *s = va_arg(v, const char *);
        printf("%s", s);
    }

    va_end(v);
}

#define print_strings(...)\
    print_strings2(COUNT_PARMS(__VA_ARGS__), __VA_ARGS__)

void count_overload1(int p1)
{
    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;
}

转载:http://blog.csdn.net/gatieme/article/details/50921577

时间: 2024-09-19 09:19:54

C语言实现函数重载的相关文章

深度探究C++中的函数重载的用法_C 语言

C++ 允许同一范围内具有相同名称的多个函数的规范.这些函数称为重载函数,"重载"中对其进行了详细介绍.利用重载函数,程序员可以根据参数的类型和数量为函数提供不同的语义. 例如,采用字符串(或 char *)参数的 print 函数执行的任务与采用"双精度"类型的参数的函数执行的任务截然不同.重载允许通用命名并使程序员无需创建名称,例如 print_sz 或 print_d.下表显示了 C++ 使用函数声明的哪些部分来区分同一范围内具有相同名称的函数组.重载注意事项

详解C++中的内联函数和函数重载_C 语言

内联函数(内嵌函数,内置函数) 调用函数时需要一定的时间和空间的开销.C++提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开.这种在函数调用处直接嵌入函数体的函数称为内联函数(inline function),又称内嵌函数或内嵌函数. 指定内联函数的方法很简单,只需要在定义函数时增加 inline 关键字. 注意:是在函数定义时增加 inline 关键字,而不是在函数声明时.在函数声明时增加 inline 关键虽然没有错误,但是也没有任何效果 inline 关键

C++函数重载详解及实例代码_C 语言

C++函数的重载 定义 在同一个作用域中,函数名相同,函数的参数列表不同的函数之间构成重载关系,在不同作用域中的同名函数遵循标识符隐藏的原则 ATTENTION:重载与函数的返回值类型无关,因为声明一个函数不需要返回类型,所以无法用来区分哪个函数 常函数和普通成员函数之间构成重载,重载时常对象调用常成员函数,一般对象调用一般成员函数 class A{ - public: void getVal()const{-} void getVal(){-} }; int main(){ const A a

让 JavaScript 轻松支持函数重载 (Part 2 - 实现)_javascript技巧

识别文本签名 我们先来回顾一下上一篇文章中提到的Overload用例: 复制代码 代码如下: var extend = Overload .add("*, ...", function(target) { }) .add("Boolean, *, ...", function(deep, target) { }); 我们允许用户输入一个字符串,表示某一个重载的签名.在用户调用函数时,我们需要拿着用户输入的参数实例去跟签名上的每一个参数类型作比较,因此我们需要先把这个

C++函数重载的深入解析_C 语言

我们在开瓶瓶罐罐的时候,经常会遭遇因各种瓶口规格不同而找不到合适的工具的尴尬.所以有时候就为了开个瓶,家里要备多种规格的开瓶器.同样是开个瓶子嘛,何必这么麻烦?于是有人发明了多功能开瓶器,不管啤酒瓶汽水瓶还是软木塞的红酒瓶都能轻松打开. 然而开瓶器的问题也会发生到程序设计中.比如我们要编写一个函数来求一个数的绝对值,然而整数.浮点型数.双精度型数都有绝对值,但为它们编写的函数返回值类型却是各不相同的.比如: 复制代码 代码如下: int iabs(int a);float fabs(float

C++语言基础 例程 函数重载

贺老师的教学链接 重载函数:同名同体,但接口不同 #include <iostream> using namespace std; int max(int a,int b,int c); //函数声明 double max(double a,double b,double c); long max(long a,long b,long c); int main( ) { int i1,i2,i3,i; cin>>i1>>i2>>i3; //输入3个整数 i=

javascript arguments与javascript函数重载

1.所 有的函数都有属于自己的一个arguments对象,它包括了函所要调用的参数.他不是一个数组,如果用typeof arguments,返回的是'object'.虽然我们可以用调用数据的方法来调用arguments.比如length,还有index方法.但是数 组的push和pop对象是不适用的. 2.函数定义时的参数个数和函数调用时的参数个数没有任何关系. 在函数中可以用f.arguments[0]和f.arguments[1]得到调用时传入的第一和第二个参数,arguments不能够创建

让JavaScript轻松支持函数重载(Part 1

JavaScript支持重载吗? JavaScript支持函数重载吗?可以说不支持,也可以说支持.说不支持,是因为JavaScript不能好像其它原生支持函数重载的语言一样,直接写多个同名函数,让编译器来判断某个调用对应的是哪一个重载.说支持,是因为JavaScript函数对参数列表不作任何限制,可以在函数内部模拟对函数重载的支持. 实际上,在很多著名的开源库当中,我们都可以看到函数内部模拟重载支持的设计.例如说jQuery的jQuery.extend方法,就是通过参数类型判断出可选参数是否存在

C++和C不同是因为函数重载

在C语言里,一个指针可以指向一个函数.这个指针也有两个属性,但一个是函数的入口地址,另一个是函数的返值类型.例如下面的程序,它在C语言里是正确的: int time12(int i) { return(i%12); } int main() { int(*fp)()=time12; intt=fp(13); return0; } 主函数的第一句是一条定义语句.我们应从等号左边的标识符读起,等号左边出现的除了标识符以外都是类型的符号,读的顺序却要按照这个符号作为运算符时的运算级别来读.此句读作:f