【问题标题】:Does C support overloading?C是否支持重载?
【发布时间】:2010-02-28 17:07:36
【问题描述】:

我只想知道C是否支持过载? 当我们使用诸如 printf 之类的系统函数时,它们的参数数量不同。 帮帮我

【问题讨论】:

标签: c


【解决方案1】:

不,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.

[*] 标准实际上并没有它们是在堆栈上传递的,或者根本没有提到堆栈,但这是自然的实现。

【讨论】:

  • 我不能代表所有编译器,但我使用的编译器让调用者在函数返回后为它推送的参数调整堆栈。对于可变参数函数,这对我来说似乎是个好主意——比让函数从堆栈中弹出它们更安全。
  • 你说得对,实际上 cdecl 调用约定就是这样工作的(调用者负责堆栈清理);我删除了评论以避免混淆其他读者。真正的问题在于 scanf,如果您提供了不正确的格式字符串,可能会在内存中的随机位置(从堆栈中获取)写入数据。
  • C1X 将支持通过类型泛型表达式和宏
【解决方案2】:

C 不支持重载。 (很明显,即使是这样,他们也不会将它用于 printf:对于每种可能的类型组合,您都需要一个 printf!)

printf 使用varargs

【讨论】:

    【解决方案3】:

    不,C 不支持重载,但它支持Variadic functions。 printf 是可变参数函数的一个示例。

    【讨论】:

      【解决方案4】:

      这完全取决于您如何定义“支持”。

      显然,C 语言在核心语言中提供了重载运算符,因为 C 中的大多数运算符都具有重载功能:您可以将二进制 +intlong 和指针类型一起使用.

      然而与此同时,C 不允许您创建自己的重载函数,并且 C 标准库还必须求助于不同名称的函数以用于不同类型(如 absfabs、@ 987654326@等)。

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

      【讨论】:

        【解决方案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);
        

        【讨论】:

          【解决方案6】:

          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))
          

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

              C 不支持重载。但是我们可以通过编写我们自己的库来实现该功能,该库反过来可以提供重载支持。

              【讨论】:

              • 不确定你的意思。提供重载支持的库会是什么样子?
              • @Carl:听起来你在谈论 vtable。那是为了继承,我不确定这对重载有什么帮助。
              • 您可以使用以 void 指针作为参数的函数来模拟重载 - 诀窍是在运行时知道如何处理这些 void 指针,您可能需要某种类型的标记(您可以考虑printf 中的格式字符串是基本类型标记)。
              【解决方案9】:

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

              【讨论】:

              • 第二。如果您使用 C++ 编译器来编译代码,那么您正在编写的是 C++ 代码,而不是 C 代码。特别是如果您使用 C++ 中没有的 C 功能,例如函数重载!
              猜你喜欢
              • 2013-04-12
              • 1970-01-01
              • 1970-01-01
              • 2019-10-22
              • 2012-04-25
              • 2012-02-11
              • 2011-09-06
              • 1970-01-01
              相关资源
              最近更新 更多