C中的函数原型

时间:2012-08-27 17:35:01

标签: c header

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

例如,此处是来自strlen的{​​{1}}声明:

string.h

在做了一些研究后,我发现extern size_t __cdecl strlen(const char *);用于在函数块之外声明变量。最佳做法是使用extern在头文件中声明我的函数吗?

我看到他们使用的extern size_t而不是unsigned long long,我假设这是因为它有几个原因(例如字符串的长度)更有效永远不会是负数)但是他们在这里使用int的原因是什么?或者我完全忽略了这一点?

然后我终于看到size_t,我找不到很多信息。什么是__cdecl?我也应该使用它吗?

最后,我注意到在这个声明中,传递给__cdecl的争论没有变量名。我猜这是因为这不是一个函数原型,只是一个声明,而原型在其他地方。为什么没有变量名称,例如声明中的strlen

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

strlen(const char *str)

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

4 个答案:

答案 0 :(得分:6)

  1. size_t更适合strlen的返回值,而不是int
  2. __cdecl是函数的调用约定。这表示谁为参数,返回值等设置堆栈以及谁清除它。更多参考:Calling convention
  3. 在声明功能时,您并不需要参数名称。只需参数类型即可。
  4. extern的更新:

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

    希望这有帮助。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:1)

关于size_t,正如@Rohan所说,它是一种用来保持大小的类型。例如,它不能是负面的。这主要是因为一些安全问题。 (例如,使用int而不是size_t导致FreeBSD中getpeername的安全漏洞)
关于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]