3

当您不包含和使用 malloc 时,我们会收到隐式声明警告。

“警告:内置函数‘malloc’的隐式声明不兼容”

此警告是由于编译器假定 malloc 定义为 int malloc(size) 而它是void* malloc(size).

但它是怎么知道的void* malloc(size)?我们没有在头文件中包含任何内容。那么如何将它与不包括在内的东西进行比较。

在那之后,我的代码是如何工作的?它如何找到正确的 malloc 定义并使用它?

扫描函数定义是否有任何顺序?

4

4 回答 4

5

当你调用一个f从未定义过的函数时,会发生这样的隐式声明:

int f();

请注意,您仍然可以将参数传递给f,因为它没有被声明int f(void);(这是为了向后兼容 K&R C)。

因此,编译器不“知道” malloc 接收大小参数,事实上你可以传递你想要的东西,它不在乎。

因此,代码有效的事实纯粹是运气。在 malloc 的情况下,如果代码有效,它只是意味着整数的大小与指针的大小相同 - 不多也不少 - 因此,您仍然可以调用 malloc 并将其结果分配给指针,因为没有位被修剪/丢失。

真正的功能是在编译发生后的链接阶段找到的。此时,您的代码已经使用错误的函数原型编译。当然,如果链接器在其路径中的任何位置都找不到该函数,则会报告错误并中止一切。

对于 malloc 和其他标准库函数,可能有内置函数来提高性能。gcc 甚至还有一个选项可以禁用内置函数(-fno-builtin-fno-builtin-function)。从 gcc 手册页:

GCC 通常会生成特殊代码来更有效地处理某些内置函数;例如,对“alloca”的调用可能成为直接调整堆栈的单个指令,对“memcpy”的调用可能成为内联复制循环。生成的代码通常更小更快,但由于函数调用不再显示为这样,因此您无法在这些调用上设置断点,也无法通过链接不同的库来更改函数的行为。此外,当一个函数被识别为内置函数时,GCC 可能会使用有关该函数的信息来警告调用该函数的问题,或者生成更高效的代码,即使生成的代码仍然包含对该函数的调用.

因此,在 malloc 的特定情况下,这就是编译器“知道”其通常签名的方式。尝试用 gcc 编译这段代码:

int main(void) {
    char *a = malloc(12, 13, 14, 15);
    return 0;
}

您将看到它将因编译错误而中止:

test.c:3: error: too many arguments to function `malloc'

如果您使用 option -fnobuiltin,错误就会消失并且警告会有所不同:

test.c:3: warning: implicit declaration of function `malloc'

每次使用以前未定义的常规函数​​时,都会收到相同的警告,因为现在编译器忽略了他对这些函数的“了解”。此示例使用 gcc,但其他编译器也会有类似的行为。

于 2013-10-13T20:57:59.420 回答
1

但它是怎么知道的void* malloc(size)?我们没有在头文件中包含任何内容。那么如何将它与不包括在内的东西进行比较。

大多数现代编译器都有一个内置的(即在编译器中硬编码)常用标准库函数列表,因此即使没有声明(显式或隐式)或调用函数,编译器也知道声明应该是什么。

这并不意味着将使用正确的声明(因为隐式声明规则会覆盖编译器的明显知识),但至少您知道自己做错了什么。

这些内置函数的确切目的是,如果您忘记包含头文件,编译器可以警告您。(还有其他目的,例如通过了解内置函数的语义/实现来执行内在优化的机会,但这些不适用于此处。)

于 2013-10-13T21:11:32.167 回答
1

您收到特定警告的原因是此特定编译器malloc中的内置函数(如警告所述)。此函数接受编译器的特殊处理。编译器对如何声明这个函数有内在的了解,所以当隐式声明与正确的声明相矛盾时,会立即发出警告。

在一般情况下,对于普通(非内置)函数,当编译器实际上发现同一函数的第一个显式声明时(假设两者之间不匹配),隐式声明的函数会出现类似的衰减。在这种情况下,警告将在稍后发出:不是在调用时,而是在显式声明时。这就是它与普通函数一起工作的方式。

但是对于像编译器这样的标准(内置)函数,malloc它允许自己有点“作弊”。

于 2013-10-13T21:20:57.260 回答
0

如果您没有正确声明它(在源代码中使用显式声明或包含正确的标头),它将找不到正确的定义。它对在声明之前调用的任何函数使用默认的隐式声明。隐式声明包括函数返回的假设int

然后它将默认声明与您调用函数的方式进行比较。它注意到您将结果分配给指针变量而不是整数,因此它警告您这些不兼容。

于 2013-10-13T20:58:52.697 回答