【问题标题】:Function Prototypes in CC 中的函数原型
【发布时间】:2012-08-27 17:35:00
【问题描述】:

今天早些时候,我浏览了各种头文件,只是为了将它们与我正在制作的头文件进行比较,并注意到它们似乎声明它们的功能有点不同。

例如这里是来自string.hstrlen 的声明:

extern size_t __cdecl strlen(const char *);

在做一些研究后,我发现extern 用于在功能块之外声明一个变量。最好在头文件中使用extern 声明我的函数吗?

我看到他们使用size_t,这里是unsigned long long,而不是int,我假设这是因为它更有效有几个原因(例如,字符串的长度永远不会是负数) 但这是他们在这里使用size_t 的原因吗?还是我完全没有抓住重点?

最后我看到了__cdecl,我找不到太多信息。 __cdecl 到底是什么?我也应该使用它吗?

最后,我注意到在这个声明中没有变量名用于传递给strlen 的争论。我猜这是因为这不是一个函数原型,只是一个声明,而原型在别处。为什么没有变量名,例如strlen(const char *str) 在声明中?

我的最后一个问题是,如果这只是一个声明,strlen 的函数原型会是什么样子?我的猜测是这样的:

size_t strlen(const char *str)

我只是问,因为我想学习和改进我的代码(假设我在 C 文件中制作函数原型/声明,然后只是在头文件中声明函数,以便其他 C 文件可以使用它们)。

【问题讨论】:

    标签: c header


    【解决方案1】:
    1. size_tstrlen 而不是 int 的更合适的返回值
    2. __cdecl 是函数的调用约定。这表示谁为参数、返回值等设置堆栈以及谁清除它。更多参考:Calling convention
    3. 在声明函数时,您实际上并不需要参数名称。只需参数类型就足够了。

    extern 的更新:

    • extern 告诉编译器该语句只是一个声明而不是定义。所以对于函数原型,extern 没有添加任何价值,因为它已经只是一个定义。参考:C Extern

    希望这会有所帮助。

    【讨论】:

    • 我真的很喜欢这个答案,但是如果您不使用参数名称,您如何实际使用函数中的给定参数?
    • 这只是原型。当你编写实际函数时,参数将被命名。
    • 啊,这真的很有意义! :-) 谢谢!
    【解决方案2】:

    extern 关键字在声明函数时是多余的,因为很明显它必须在其他地方定义。对于没有extern 的声明和定义之间没有区别的变量,情况并非如此。

    【讨论】:

      【解决方案3】:

      size_t strlen 定义返回的内容。在您的情况下,它似乎也像一个 64 位系统,理论上一个字符串可以大于可以存储在 int 中的字符串。

      __cdecl 是编译标准库时使用的调用对流。如果您应该为程序选择不同的调用约定(使用一些编译器选项),预编译的库函数仍将使用正确的约定。

      我经常在函数声明中命名所有参数,因为它有助于记录它们是什么。只是int f(int, int, int, int) 对我没有多大帮助,但对编译器来说已经足够了。因此名称是可选的。

      【讨论】:

        【解决方案4】:

        关于@Rohan 所说的 size_t ,它是一种用于保存大小的类型。例如,它不能是负面的。这主要是因为一些安全问题。 (例如 FreeBSD 中 getpeername 中的安全漏洞是由使用 int 而不是 size_t 引起的)
        关于 cdecl 这可能会对您有所帮助(来自PC assembly book):

        Borland 和 Microsoft 使用通用语法声明 calling conventions。他们将 cdecl 和 stdcall 关键字添加到 C 中。这些关键字充当函数修饰符,并立即出现在原型中的函数名称之前。例如,对于 Borland 和 Microsoft,函数 f 将定义如下:

        void __cdecl f (int);

        每个调用约定都有优点和缺点。 cdecl 约定的主要优点是简单且非常灵活。它可以用于任何类型的 C 函数和 C 编译器。使用其他约定可能会限制子例程的可移植性。它的主要缺点是它可能比其他一些慢并且使用更多内存(因为函数的每次调用都需要代码来删除 堆栈上的参数)。

        stdcall 约定的优点是它使用更少的内存 比cdecl。在 CALL 指令之后不需要进行堆栈清理。它的主要 缺点是它不能与具有变量的函数一起使用 参数的数量。 [赞printf]

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-06-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-06
          • 1970-01-01
          • 2012-07-15
          相关资源
          最近更新 更多