32

会议记录:

> type lookma.c
int main() {
  printf("%s", "no stdio.h");
}

> cl lookma.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

lookma.c
Microsoft (R) Incremental Linker Version 8.00.50727.762
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:lookma.exe
lookma.obj

> lookma
no stdio.h
4

3 回答 3

39

您最初标记了这个 C++,但它似乎是一个 C 程序。如果范围内没有原型(例如由于省略#include <stdio.h>),C 将自动为函数提供隐式声明。隐式声明将是:

int printf();

这意味着 printf 是一个返回 int 并且可以接受任意数量的参数的函数。这个原型碰巧适合你的电话。你应该#include <stdio.h>

最后,我应该补充一点,当前的 C 标准(ISO/IEC 9899:1999 或通俗地称为“C99”)不允许隐式声明,并且该程序不符合。隐式声明被删除。我相信你的编译器不支持 C99。C++ 还需要正确的原型并且不进行隐式声明。

于 2008-12-03T11:01:25.693 回答
33

在严格合规模式下(这意味着“理论上”),当您调用一个函数时调用未定义的行为(这很糟糕),该函数在范围内没有函数的原型声明,而该函数采用可变数量的参数。这意味着编译器可以对printf()没有原型 from#include <stdio.h>或等效声明的程序做任何它喜欢的事情。“任何喜欢的东西”包括作为选项之一正常工作;这似乎是您的示例选择的选项。

在实践中,即使没有函数的正式声明,代码也可以在大多数实用编译器中printf()正常工作。

正如 qrdl 所指出的,找到该函数是因为 C 编译器与 C 库链接。

请注意,Chris Young 关于 C99 和“隐式 int”的评论是准确的,但关于“变量参数函数必须在范围内具有原型”的规则适用于 C89 和 C99。大多数编译器在默认情况下不能在严格的 C99 兼容模式下工作,因为有太多的代码不会像那样编译。

克里斯·杨评论道:

为了澄清,我的评论是关于 C99 删除隐式声明。通过说“隐式 int”,我认为您指的是允许诸如 foo(void); 之类的声明的 C89 功能;表示 int foo(void);,C99 也删除了一些东西。

克里斯当然是正确的。从 C99 标准中删除了两个“隐式声明”功能。该标准的前言将它们列为:

  • 删除隐式int
  • 删除隐式函数声明

我没有足够清楚地思考(因此没有写作)。尽管如此,C89 和 C99 都需要在函数范围内有一个原型,用于接受可变数量参数的函数。

为了显示:

extern int pqr();
int main(void)
{
    int i = pqr(1, 3);
    return i;
}

如果没有第一行,这是一个正确的 C89 片段,其中将函数隐式声明为pqr()返回整数(带有未指定参数)的函数。如果第一行替换为extern pqr();,则这是一个正确的 C89 片段,其中显式声明pqr()为返回整数(带有未指定参数)的函数,但返回类型为“隐式int”。如所写,该函数被显式声明并具有显式int返回类型 - 但它仍然具有未指定的参数。我相信这是有效的 C99 - 尽管不是完全可取的。当然,GCC (3.4.4) 接受它并带有选项 ' -std=c99 -pedantic"。理想情况下,函数声明应该包括完整的原型。(并且,如果pqr()用省略号定义,理论上需要该原型!)

于 2008-12-04T20:37:13.847 回答
10

printf()位于标准 C 库中,链接器始终将标准库链接到您的可执行文件,因此将找到任何标准函数并且不会出现链接问题。

未能包含适当的标头会导致使用未原型化的函数,这可能会导致问题,因为 C 编译器假定没有原型的函数返回int并采用可变数量的参数。所以总是包括标题 - 这是你的安全围栏。

于 2008-12-03T11:16:45.613 回答