怎么可能在 C++ 中的 main() 中不声明任何内容,但在编译后有一个工作应用程序?

Posted

技术标签:

【中文标题】怎么可能在 C++ 中的 main() 中不声明任何内容,但在编译后有一个工作应用程序?【英文标题】:How is it possible to declare nothing inside main() in C++ and yet have a working application after compilation? 【发布时间】:2013-07-05 23:52:42 【问题描述】:

在一次采访中,我遇到了这样一个问题:

你的朋友给了你一个源代码文件,它打印 控制台上的斐波那契数字。请注意, main() 块是 空的,里面没有任何语句。

解释这是怎么可能的(提示:全局实例!)

我真的很想知道,这怎么可能!

【问题讨论】:

看提示! 因为它是 1) 我没听说过的东西,2) 是有用的琐事,因为人们在采访中问它,3) 一个有趣的语言应用程序,以便 4) 我可以识别如果我看到他们在生产代码中实际使用它,就会用生锈的刀刺伤任何人的脸。 一个称职的专业 C++ 程序员会知道这个问题的答案。如果这个 interview 问题的目的是确定被采访者是否是一个称职的、专业的 C++ 程序员,那么这个问题不应该给他们答案。 在面试设置中,一种替代方法是将逻辑包含在代码中的任何函数中,并使用assert#pragma message 等记录输出。这将在期间将输出重定向到控制台汇编。该程序甚至可能永远无法完全编译,但这无疑是一种有趣的方式,可以在面试期间展示您的“开箱即用”思维。这满足了引用的问题,因为它没有提及有关生成二进制的任何内容;相反,它只是谈论可以在控制台上显示“东西”的 C 文件。 ;-) 是IOCC的采访吗? :-) 好吧,我承认我经常这样做是为了初始化我的工厂或执行一些测试代码。顺便说一句,'single source code file' 也是一个提示,即 entry-pint(默认为 main)不会被链接器替换。 【参考方案1】:

它很可能被实现为(或其变体):

 void print_fibs() 
 
       //implementation
 

 int ignore = (print_fibs(), 0);

 int main() 

在此代码中,全局变量ignore 必须在进入main() 函数之前进行初始化。现在为了初始化全局,print_fibs() 需要在你可以做任何事情的地方执行——在这种情况下,计算斐波那契数并打印它们!我在以下问题中展示了类似的东西(我很久以前就问过):

Is main() really start of a C++ program?

请注意,此类代码不安全,一般情况下最好避免使用。例如,std::cout 对象在执行print_fibs() 时可能没有被初始化,如果是这样,那么std::cout 在函数中会做什么?但是,如果在其他情况下,它不依赖于这样的初始化顺序,那么调用初始化函数是安全的(这是 C 和 C++ 中的常见做法)。

【讨论】:

@Nawaz 可能值得引用确切的保证。翻译单元中的对象保证按顺序初始化。标准流对象保证在std::ios_base::Init 对象的第一次初始化之前或期间进行初始化。并且<iostream> 保证表现得“好像”它在命名空间范围内包含std::ios_base_Init 对象的实例。 @Steve314:它不返回任何内容,这就是我使用逗号运算符的原因,以确保整个表达式(print_fibs(), 0) 的类型为int。这里是Online Demo。 @Nawaz void 函数和逗号运算符的替代方法是返回 bool 和变量 bool fibsPrintedif 该函数仅在此处起作用,这可能会稍微干净一些。 (但差异可能不足以担心。) +1,谈论真棒。不得不加入 *** 只是为了支持这个问题和这个答案。 @Nawaz 我不确定你的意思是什么。 std::cout 的定义在库中的某处。但正如我已经指出的,标准 要求std::ios_base::Init 对象的第一个构造函数完成之前对其进行初始化,并且它要求包含 <iostream> 的行为就像 std::ios_base::Init对象是在命名空间范围内定义的。如果翻译单元在初始化对象的定义之前包含<iostream>,则保证构造std::cout【参考方案2】:

希望对你有帮助

class cls

  public:
    cls()
    
      // Your code for fibonacci series
    
 objCls;

int main()


因此,只要声明了类的全局变量,就会调用构造函数并在其中添加逻辑以打印出斐波那契数列。

【讨论】:

【参考方案3】:

是的,这是可能的。 您需要在对象构造函数中声明一个计算斐波那契数的对象的全局实例。

【讨论】:

您需要声明一个对象的全局实例,其初始化程序计算斐波那契数。【参考方案4】:

我知道一些你所说的例子。获得它的一种方法是使用模板元编程。使用它,您可以将一些计算过程移动到编译中。

Here你可以得到一个斐波那契数列的例子

如果你在静态类构造函数中使用它,你可以写数字而不需要在主函数中写任何代码。

希望对你有帮助。

【讨论】:

【参考方案5】:

在全局/静态变量的初始化过程中可能会发生一些事情。该代码将在应用程序启动时触发。

【讨论】:

【参考方案6】:

文件范围对象的所有 [*] 构造函数在到达 main 之前被调用,非对象文件范围变量的所有初始化表达式也是如此。

编辑:此外,在main 退出后,所有文件范围对象的所有[*] 析构函数都以相反的构造顺序被调用。从理论上讲,您可以将斐波那契程序放入对象的析构函数中。

[*] 请注意,“all”会忽略动态加载和卸载程序未直接链接的库的行为。但是,从技术上讲,这些不在基本 C++ 语言范围内。

【讨论】:

全部?即使是那些在main之后显式加载的dll? 好吧,C++ 在技术上并没有定义动态加载的库,所以在纯 C++ 中,我的说法是正确的。因此,将其遮蔽“全部,保存在到达 main 后加载的 DLL/DSO 中包含的初始化程序和文件范围对象。”在这种情况下,main 是空的,因此这些 DLL/DSO 必须由析构函数加载,这是非常不正常的。但是,这是计算机科学,我想我们应该小心“全部”之类的词。 我在上面的答案中添加了关于“全部”的警告,还添加了关于 dtors 的注释。 是的,但希望那会到来。 Pre C++11 包含一些允许 DLL 的狡猾的措辞,但实际上这仅意味着从技术上讲,保证并不总是存在,即使它存在于所有实际实现中,并且很多代码都依赖于它。至少,C++11 修复了这个问题。

以上是关于怎么可能在 C++ 中的 main() 中不声明任何内容,但在编译后有一个工作应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中的 main() 函数中声明的类

C++ 的 main.cpp 中的结构声明问题

C++函数的声明和定义不同但效果很好

C++ --- 写个函数在main函数执行前先运行

int main() 是不是需要在 C++ 上声明?

什么是 C++ 中的前向声明?