c程序错误,两个函数具有相同的返回类型和名称但功能签名不同

时间:2015-12-17 06:30:49

标签: c

这是否允许在c?

void hello(int a, float y)
{


    int s = a + y;
    printf("value in hello = %d \n",s);

}

void hello(int a, int b, int c)
{


    int s = a + b + c;
    printf("value in hello 2 = %d \n", s);

}


int main(void) {
    // your code goes here
    hello(2, 3.5);
    hello(1, 2, 3);
    return 0;
}

这里的功能签名不同吗?

我收到错误:

prog.c:11:6: error: conflicting types for 'hello'
 void hello(int a,int b,int c)
      ^
prog.c:2:6: note: previous definition of 'hello' was here
 void hello(int a,float y)
      ^
prog.c: In function 'main':
prog.c:24:2: error: too few arguments to function 'hello'
  hello(2,3.5);
  ^
prog.c:11:6: note: declared here
 void hello(int a,int b,int c)

5 个答案:

答案 0 :(得分:4)

没有。这不被允许。 C不允许函数重载。

你最接近的是写一个variadic functions.

例如,Unix open(2)系统调用假装以支持两个不同的接口:

   int open(const char *pathname, int flags);
   int open(const char *pathname, int flags, mode_t mode);

但事实上,它是一种可变函数。

答案 1 :(得分:1)

这是一个名为函数重载的功能。它不允许使用C语言。

答案 2 :(得分:1)

不允许。

C不像现代OOP(C#或Java),其中允许使用具有相同名称但签名不同的方法。

此功能称为function overloading,在C中不可用。

你可以阅读这个

Does C support overloading?

了解更多详情。

答案 3 :(得分:1)

正如其他几个海报已经说过的那样,在平原C中不支持。但是你可以使用一些技巧。如果您使用的是C11,则可以使用_Generic个宏。当被调用函数根据参数的类型具有不同的行为时,这是可用的。

#include <stdio.h>
void hello_f( float f ){ printf("I'm a float: %f\n", f ); }
void hello_i( int i ){ printf("I'm an integer: %d\n", i ); }
#define hello(x) _Generic((x), int: hello_i, float: hello_f)(x)
int main(){
    hello( 2 );
    hello( 2.0f );
    return 0;
}

像这样工作:

$ gcc -std=c11 generic_test.c && a.exe
I'm an integer: 2
I'm a float: 2.000000

如果签名的参数数量不同,还有另一种模仿重载行为的技巧。这个技巧可以在C99中使用,因为它使用了可变参数宏。

/* Your original functions renamed */
void hello_int_float(int a,float y){
    int s = a + y;
    printf("value in hello = %d \n",s);
}

void hello_int_int_int(int a,int b,int c){
    int s = a + b + c;
    printf("value in hello 2 = %d \n",s);
}

#define VA_WHAT_IMPL(_1, _2, _3, N, ...) N
#define VA_WHAT(...) VA_WHAT_IMPL(__VA_ARGS__, int_int_int, int_float, one_arg)

#define hello_impl2(signature_count, ...) hello_ ## signature_count (__VA_ARGS__)
#define hello_impl(what, ...) hello_impl2(what, __VA_ARGS__) 
#define hello(...) hello_impl(VA_WHAT(__VA_ARGS__), __VA_ARGS__)

int main(){
    hello( 2, 1.0f);
    hello( 1, 3, 6);
    return 0;
}

请注意,如果不支持参数数量或参数类型不正确,您会收到有用的错误消息,这种方法并不是真正安全的。 它只计算参数的数量。以下是代码扩展到的内容:

$ gcc -std=c99 -E overload.c
# 1 "overload.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "overload.c"

void hello_int_float(int a,float y){
    int s = a + y;
    printf("value in hello = %d \n",s);
}
void hello_int_int_int(int a,int b,int c){
    int s = a + b + c;
    printf("value in hello 2 = %d \n",s);
}
# 19 "overload.c"
int main(){
    hello_int_float (2, 1.0f);
    hello_int_int_int (1, 3, 6);
    return 0;
}

我实际上在实际代码中使用了这个技巧。

答案 4 :(得分:1)

正如其他人所说,C不支持超载。我会试着告诉你原因。

如果你编译:

// file1.c
int helloi(int a) { 
    return a; 
}

int hellofff(float a, float b, float c) {
    return a * b * c;
}

gcc -c file1.c

您可以检查输出文件的符号:

$ nm file1.o 
000000000000000c T hellofff
0000000000000000 T helloi

函数名称与代码中的函数名称完全相同。这是因为C保留了用于链接的函数和全局变量名。

让我们使用重载在C ++中做同样的事情

// file2.cpp
int hello(int a) {
    return a;
}

int hello(float a, float b, float c) {
    return a * b * c;
}

编译并检查它的符号:

$ g++ -c file2.cpp 
$ nm file2.o 
000000000000000c T _Z5hellofff
0000000000000000 T _Z5helloi

它完成了我们在C中手动完成的操作。它还在前面插入了_Z5_Z表示&#34;这是一个GCC错位名称&#34;。 5表示实际名称长度为5个字节,之后是类型。 (否则你不会告诉helloi()和hello(int)之间的区别。)

我们甚至可以称这些&#34; Mangled&#34; (技术术语)来自C的名字:

// file3.c
#include <stdio.h>

int _Z5hellofff(float a, float b, float c);

int main(void) {
    printf("hello(1, 2, 3) == %d\n", _Z5hellofff(1, 2, 3));
    return 0;
}

并使用:gcc -o file3 file3.c file2.o

进行编译

希望它链接和运行。还有一些其他格式,例如Microsoft,Go,Rust等使用的格式。 (Rust也使用_ZNname,但之后很难解读胡言乱语

_Z开头的原因是在C中,编译器/ libc保留以_Z开头的标识,用户不应该将它们用于自己的事物(它在这里没关系,因为我们知道编译器将生成什么)。 (至少在gcc / clang中以__开头的事情)

C ++也有名称空间,因此namespace::class::thing(int)变为_Z20namespace5class5thingi