2010-02-28 90 views
50

我只想知道C是否支持加载? 由于我们使用printf等系统函数,其参数不同。 帮我出来C支持重载吗?

+2

Duplicate:http://stackoverflow.com/questions/479207/function-overloading-in-c – 2010-03-20 10:25:30

+0

请参阅Leushenko 2014年答案使用C11 _泛型类型选择器在愚蠢http://stackoverflow.com/questions/479207/function -overloading-在-C。 – 2015-04-16 07:52:21

回答

44

不,C不支持任何形式的超载(除非你是内置运营商重载已经是事实,是的超载一种形式)。

printf使用称为varargs的功能工作。你让一个呼叫看起来像它可能过载:

printf("%d", 12); // int overload? 
printf("%s", "hi"); // char* overload? 

其实事实并非如此。只有一个printf函数,但编译器使用特殊的调用约定来调用它,其中提供的任何参数都按顺序放置在堆栈上[*]。 printf(或vprintf)检查格式字符串并使用它来计算出如何读取这些参数。这就是为什么printf的是不是类型安全:

char *format = "%d"; 
printf(format, "hi"); // undefined behaviour, no diagnostic required. 

[*]标准实际上并不他们在栈中传递,或提栈可言,但是这是自然的实现。

+0

我不能为所有编译器说话,但是一旦函数返回,与我一起工作的那个调用者就会调整它所推送的参数的堆栈。对于我来说,这对于可变参数函数来说似乎是一个好主意 - 比使函数从堆栈中弹出它们更安全。 – tomlogic 2010-02-28 17:42:14

+1

你说得对,实际上cdecl的调用约定就是这样的(调用者负责堆栈清理);我删除了评论以避免混淆其他读者。真正的问题在于scanf,如果提供的格式字符串不正确,可能会将数据写入内存中的随机位置(从堆栈中取出)。 – 2010-02-28 17:47:31

+0

C1X将支持通过类型通用表达式和宏 – Spudd86 2011-07-22 22:30:11

29

C不支持重载。 (显然,即使它确实如此,它们也不会将它用于printf:对于每种可能的组合类型,您都需要printf!)

printf使用varargs

0

C不支持重载。但是我们可以通过编程我们自己的库来实现这个功能,而这个库又可以提供超载支持。

+2

不确定你的意思。提供重载支持的库看起来像什么? – sepp2k 2010-02-28 17:12:48

+1

@ sepp2k,一堆函数指针表。 – 2010-02-28 18:10:08

+0

@Carl:听起来你正在谈论一个虚拟表。这是为了继承,我不确定这将如何帮助超载。 – sepp2k 2010-02-28 18:13:50

5

不,C不支持重载。如果你想实现类似于C++的重载,你将不得不手动地调整你的函数名称,使用某种一致的约定。例如:

int myModule_myFunction_add(); 
int myModule_myFunction_add_int(int); 
int myModule_myFunction_add_char_int(char, int); 
int myModule_myFunction_add_pMyStruct_int(MyStruct*, int); 
8

这一切都取决于您如何定义“支持”。

显然,C语言提供重载运算核心语言中,因为使用C大多数运营商都重载功能:您可以使用二进制+intlong与指针类型。

然而,在同一时间C不允许你创建你自己的重载函数和C标准库也有诉诸不同名称的功能与不同类型(如absfabslabs等使用)。

换句话说,C有一定程度的重载编码为核心语言,但标准库和用户都不允许自己重载。

-5

没有c不支持函数重载。 但是如果你使用g ++(一个C++编译器),你可以把它编译/工作。

+3

-1,如果你正在编写C你应该使用C编译器。 – 2010-02-28 18:51:08

+4

借调。如果您使用C++编译器来编译代码,那么您正在撰写的是C++代码,而不是C代码。特别是如果您使用C++中不在C中的功能,例如功能重载! – 2010-02-28 21:40:37

+0

认真-2哈哈 – chinmaya 2010-03-01 02:14:20

1

不是直接的,这不是printf的工作方式,但如果类型大小不同,则可以使用宏创建等效的重载函数。 C99标准的tgmath.h中的类型通用数学函数可以用这种方式实现。

1

C标准没有规定操作符重载;由于许多构建系统无法容纳具有相同名称的多个功能,所以添加它的建议已被拒绝。虽然C++可以通过例如有

void foo(int); 
int foo(char*); 
long foo(char *, char **); 

编译成一个名为类似v__foo_i,i__foo_pc和l__foo_pc_ppc [编译器使用不同的命名规则的功能,虽然C++标准禁止在标识符利用内部双下划线的,以使编译器给东西的名字像上面没有冲突]。 C标准的作者不希望任何编译器改变命名约定以允许重载,所以他们不提供它。

编译器将允许重载静态和内联函数而不会产生命名问题;这将在实际中是一样允许外部联性功能超载为有用的,因为人们可以具有一个头文件:

void foo_zz1(int); 
int foo_zz2(char*); 
long foo_zz3(char *, char **); 
inline void foo(int x) { foo_zz1(x); } 
inline int foo(char* st) { foo_zz2(st); } 
long foo(char *p1, char **p2) { foo_zz3(p1,p2); } 

我记得在看为C和C++之间的混合的嵌入式编译器,其支持上述的作为一个非标准的延伸,但我对细节并不积极。无论如何,即使一些C编译器支持重载没有外部链接的函数的重载,C14也不支持它,但我不知道(不幸的是)(不幸)有任何积极的努力将这种特性添加到未来的C标准中。

尽管如此,GCC可以通过使用宏来支持一种重载形式,这种形式的重载不会直接在具有运算符重载的语言中被支持。 GCC包含一个内在的特征,它将确定一个表达式是否可以作为编译时常量进行评估。使用这个内在的,可以写一个宏,它可以根据参数以不同的方式(包括通过调用函数)来评估表达式。在某些情况下,如果给定编译时常量参数,公式将作为编译时常量进行计算,但在给定可变参数时会产生可怕的混乱情况。作为一个简单的例子,假设有人希望位反转一个32位的值。如果该值是恒定的,一个能做到这一点通过:

#define nyb_swap(x) \ 
    ((((x) & 1)<<3) | (((x) & 2)<<1) | (((x) & 4)>>1) | ((((x) & 8)>>3)) 
#define byte_swap(x) \ 
    ((nyb_swap(x)<<4) | nyb_swap((x) >> 4)) 
#define word_swap(x) \ 
    ((byte_swap(x)<<24) | (byte_swap((x) >> 8)<<16) | \ 
    (byte_swap((x) >> 16)<<8) | (byte_swap((x) >> 24))) 

而像uint32_t x=word_swap(0x12345678);表达只会加载x与0×87654321。另一方面,如果该值不是一个常数,结果将是可怕的:像uint32_t y=word_swap(x);这样的表达式可能会产生许多指令;使用部分展开的循环调用函数几乎同样快,但更紧凑。另一方面,使用循环会阻止将结果视为编译时常量。

使用GCC,可以定义一个宏,这将既可以使用固定收益宏,如果在恒定的,或者给一个变量时调用一个函数:

#define wswap(x) \ 
    (__builtin_constant_p((x)) ? word_swap((x)) : word_swap_func((x)) 

这种方法不能做的一切类型基于重载可以做,但它可以做很多事情,重载不能。