【发布时间】:2010-02-28 17:07:36
【问题描述】:
我只想知道C是否支持过载? 当我们使用诸如 printf 之类的系统函数时,它们的参数数量不同。 帮帮我
【问题讨论】:
-
查看 Leushenko 的 2014 年答案,在欺骗 stackoverflow.com/questions/479207/function-overloading-in-c 中使用 C11 _Generic 类型选择器。
标签: c
我只想知道C是否支持过载? 当我们使用诸如 printf 之类的系统函数时,它们的参数数量不同。 帮帮我
【问题讨论】:
标签: c
不,C 不支持任何形式的重载(除非您将内置运算符 重载 算作 重载 的一种形式)。
printf 使用称为可变参数的功能工作。您拨打的电话看起来可能会过载:
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.
[*] 标准实际上并没有说它们是在堆栈上传递的,或者根本没有提到堆栈,但这是自然的实现。
【讨论】:
C 不支持重载。 (很明显,即使是这样,他们也不会将它用于 printf:对于每种可能的类型组合,您都需要一个 printf!)
printf 使用varargs。
【讨论】:
不,C 不支持重载,但它支持Variadic functions。 printf 是可变参数函数的一个示例。
【讨论】:
这完全取决于您如何定义“支持”。
显然,C 语言在核心语言中提供了重载运算符,因为 C 中的大多数运算符都具有重载功能:您可以将二进制 + 与 int、long 和指针类型一起使用.
然而与此同时,C 不允许您创建自己的重载函数,并且 C 标准库还必须求助于不同名称的函数以用于不同类型(如 abs、fabs、@ 987654326@等)。
换句话说,C 有一定程度的重载硬编码到核心语言中,但标准库和用户都不允许自己进行重载。
【讨论】:
不,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);
【讨论】:
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 加载为0x87654321。另一方面,如果值不是常数,结果会很糟糕:像uint32_t y=word_swap(x); 这样的表达式可能会生成几十条指令;使用部分展开的循环调用函数几乎一样快,但更紧凑。另一方面,使用循环会阻止结果被视为编译时常量。
使用 GCC,可以定义一个宏,如果给定一个常量,它将使用产生常量的宏,或者在给定一个变量时调用一个函数:
#define wswap(x) \
(__builtin_constant_p((x)) ? word_swap((x)) : word_swap_func((x))
这种方法不能做所有基于类型的重载可以做的事情,但它可以做很多重载不能做的事情。
【讨论】:
不是直接的,这不是printf 的工作方式,但是如果类型的大小不同,则可以使用宏创建等效的重载函数。 C99 标准的tgmath.h 中的类型通用数学函数可以以这种方式实现。
【讨论】:
C 不支持重载。但是我们可以通过编写我们自己的库来实现该功能,该库反过来可以提供重载支持。
【讨论】:
No c 不支持函数重载。 但是如果你使用 g++(一个 c++ 编译器),你可以让它编译/工作。
【讨论】: