C是否等同于Python's: if __name__ == '__main__': to write main() at the top of the file?

Is There C equivalent to Python's: if __name__ == '__main__': to write main() at the top of the file?

在 C 中,在 main 之前编写函数声明似乎是多余的。

我不想使用这些方法:

  1. 将函数写在不同的文件中,然后在顶部导入。
  2. 将整个函数写在main之前。
if __name__ == '__main__':
    main()

C 的实现方式是什么?

#include <stdio.h>

void printHello(void);

int main(void)
{
    printHello();
}

void printHello(void)
{
    printf("Hello World\n");
}

环境: 我正在使用 make 脚本,该脚本使用 clang 并在哈佛的 cs50 云中进行额外检查 IDE。 IDE 是使用 c99 版本的语言设置的,没有函数声明就无法编译。

我的测试: 仅使用 clang 仍然会出错;使用 GCC 实际上只编译了一个警告。

为什么 GCC 有效? GCC 看到一个没有前面声明的函数调用,它假定函数返回 int 并编译。(c99 之前的行为)

已解决: c99 定义消除了隐式类型,因此所有函数都必须在使用前声明。 对于在文件顶部编写 main() 的使用,C 中没有等效项:if __name__ == '__main__':

感谢约翰博德的帮助;现在说得通了。

在C语言中,调用的第一个函数必须具有以下原型:

int main(int argc, char* argv[])

其中argc是参数个数,argv[]是包含参数的char*数组
如果您不关心参数,您还可以声明以下主要内容:

int main()

您确实需要进行前向声明(如果您希望将 main 保留在文件开头),因为此代码将被编译而不是执行。当编译器逐行编译时,它在到达之前不会知道函数的签名。

人们习惯于在文件底部开发更重要的功能。

但是编译是如何工作的?

编译实际上分两步完成。

1) Gcc(或任何您使用的)将为您拥有的每个文件制作一个预编译版本。

2) 它将所有这些预编译文件链接到可执行文件或库文件等...

既然你知道了这一点,你就可以理解它是如何工作的了:

  • 第一步,编译器只能知道单个文件中存在的函数/变量,而不知道其他的,所以如果你调用一个不在文件中的函数 ==> 他会不高兴并生成 "undeclared function" 错误。但是如果你给他提供头文件,基本上就是告诉编译器"don't mind about this function missing, I'll add it later, in another file, at linking time"。所以编译器会忽略这个错误。
  • 在第二步,当将所有文件链接在一起时,它会再次检查您是否信守诺言,为他提供头文件中定义的函数的实现。否则,你会得到另一种错误,比如 "could not find function XXX"(我的错误名称不准确,我已经一年没学过 C 了)。

一个python文件也是一个python模块。如果你想在模块的命名空间中使用某些东西,你通常会导入它。 if __name__ == “__main__” 结构通常用于将模块视为可执行文件(用于测试等)。对于像 C 这样的编译语言,这是通过在 .c 文件中调用 main 来实现的。

已接受的答案包含严重错误。 C 编译器不会 知道文件中的每个顶级声明。他们只知道以文本形式出现在他们当前正在处理的声明上方的顶级声明。用数据定义很容易证明这一点:

// This code will not compile
const char *const strings[] = { sThis, sThat };
static const char sThis[] = "this";
static const char sThat[] = "that";

上面的片段会出现硬错误,因为当编译器处理 strings 时,sThissThat 不可用.将 strings 的定义移动到 sThissThat 的定义下方是修复错误的唯一方法。

然而,对于函数,"implicit function declaration" 的遗留特性混淆了这个问题。在大多数 C 编译器的默认模式下,这将编译,可能带有警告:

#include <stdio.h>

int main(void)
{
    printHello();
}

void printHello(void)
{
    printf("Hello World\n");
}

因为编译器在调用表达式中第一次遇到 printHello 时会猜测它的类型签名。但是,猜测总是 错误: 隐式声明的函数总是被理解为采用 未指定数量的参数 和 return int,这绝对不是您想要的。编译器会欣然接受

#include <stdio.h>

int main(void)
{
    return printHello(1, "frobozz", 3.14159)
         + printHello(2.1828);
}

void printHello(void)
{
    printf("Hello World\n");
}

尽管这显然是无稽之谈。

在 C 中做 OP 想做的事情的正确方法是使用 OP 不喜欢的 "redundant" 前向声明:

#include <stdio.h>

static void printHello(void);

int main(void)
{
    printHello();
}

static void printHello(void)
{
    printf("Hello World\n");
}

如果您想将 printHello 的定义放在下面main.

在C语言中,每个程序的入口点都是main函数1,不需要特别标记,定义为[=22即可=]

int main( void ) { ... }

如果您不接受任何命令行参数,或者

int main( int argc, char **argv ) { ... }

如果你是。

在 C 中,所有函数在使用前必须至少声明。在旧版本的 C 中,如果编译器看到一个没有前面声明的函数调用,它会假定该函数返回 int。但是,1999 版的语言定义取消了隐式类型,因此所有函数 必须 在使用前声明。

函数定义算作声明,函数可以在源文件中以任意顺序定义。我总是建议,如果它们都在同一个翻译单元2,那么被调用的函数在它们的调用者之前定义,比如

#include <stdio.h>

int foo( int x ) { return 2 * x; }

int bar( int x ) { return foo( x ) * 3; }

int main( void )
{
  printf ( "bar( %d ) = %d\n" , 2, bar(2) );
  return 0;
}

这意味着您的代码显示为 "backwards",底部有 main,但是 IME 这使得代码更易于维护,因为您不必弄乱单独的声明。

如果一个函数是在不同的翻译单元(源文件)中定义的,那么您将需要一个单独的声明。我们通常通过将这些声明收集到一个单独的 头文件 中来做到这一点,然后在必要时 #include-ing 该文件:

bar.h:

#ifndef BAR_H // include guard - not required, but good practice.
#define BAR_H // Keeps the contents of the file from being processed more than once in a translation unit

int foo( int ); // only need the type of the argument in a declaration

int bar( int );

#endif

bar.c:

#include "bar.h"

int foo( int x ) { return 2 * x; }

int bar( int x ) { return foo( x ) * 3; }

main.c

#include <stdio.h>
#include "bar.h"

int main( void )
{
  printf( "bar( %d ) = %d\n", 2, bar(2) );
  return 0;
}

如何将函数划分为单独的源文件和头文件取决于项目。

您可能已经注意到我在 stdio.h 周围使用尖括号并在 bar.h 周围使用引号。不同的定界符指示编译器在何处查找包含文件的不同搜索路径。引号表示先搜索当前工作目录,再搜索标准搜索路径中的其他目录(用尖括号表示)。


  1. 无论如何,在托管实施中。在独立的实现中,入口点可以命名为其他名称。
  2. 翻译单元是执行完所有预处理指令后的源文件。