今天早些时候,我正在查看各种头文件,只是为了将它们与我正在制作的文件进行比较,并注意到它们似乎宣布它们的功能有点不同。
例如,此处是来自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文件可以使用它们)。
答案 0 :(得分:6)
size_t
更适合strlen
的返回值,而不是int
__cdecl
是函数的调用约定。这表示谁为参数,返回值等设置堆栈以及谁清除它。更多参考:Calling convention 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
]