【问题标题】:in c++ main function is the entry point to program how i can change it to an other function?在 C++ 中,主函数是编程的入口点,我如何将其更改为其他函数?
【发布时间】:2011-04-27 20:24:27
【问题描述】:

我被问到一个面试问题,将 C 或 C++ 程序的入口点从 main() 更改为任何其他函数。怎么可能?

【问题讨论】:

  • 你没有。它必须要么是main,要么是一些实现定义的入口点。没有标准的方法。
  • 取决于您的编译器/链接器。
  • 程序的入口点是它在机器代码级别开始执行的地方。这很少,如果有的话main;相反,入口点函数执行一些初始化任务,然后,对于 C 或 C++ 程序,调用 main。所以这个问题没有意义。你确定那是确切的问题吗?
  • 不是一个好的面试问题。最好的答案是“我不知道”。
  • 我的回答(尽管它可能无法让我得到这份工作)是“当你想出这个问题时,我能得到一点你在抽烟的东西吗?” :-)

标签: c++ c main


【解决方案1】:

在标准 C(我相信 C++ 也是如此)中,您不能,至少对于托管环境不能(但见下文)。标准规定 C 代码的起点是main。标准(c99)并没有留下太多争论的余地:

5.1.2.2.1 程序启动:(1)程序启动时调用的函数名为main。

就是这样。然后它对参数和返回值进行了一些讨论,但实际上没有任何改变名称的余地。

这是针对托管环境的。该标准还允许一个独立的环境(即,没有操作系统,例如嵌入式系统)。对于独立的环境:

在独立环境中(C 程序的执行可能在没有任何操作系统优势的情况下发生),程序启动时调用的函数的名称和类型是实现定义的。除了第 4 条要求的最小集合之外,独立程序可用的任何库设施都是实现定义的。

您可以在 C 中使用“诡计”实现,这样您就可以让它看起来像 main 不是入口点。这实际上是早期 Windows 编译器将WinMain 标记为起点的做法。


第一种方式:链接器可能会在start.o 之类的文件中包含一些主启动前代码,而正是这段代码运行以设置C 环境,然后调用main。没有什么可以阻止您将其替换为调用 bob 的东西。


第二种方式:一些链接器通过命令行开关提供该选项,这样您就可以在不重新编译启动代码的情况下更改它。


第三种方式:可以链接这段代码:

int main (int c, char *v[]) { return bob (c, v); }

然后你的你的代码的入口点似乎是bob而不是main


然而,所有这一切,虽然可能具有学术兴趣,但并没有改变这样一个事实,即在我数十年的代码剪切过程中,我想不出一个单独的情况,这是必要的或可取的。

我会问面试官:你为什么想要这样做?

【讨论】:

  • +1 用于制作main 只需调用别的东西。这是实现这一目标的最直接和跨平台的方法。
  • 难道不能用全局对象的构造函数做一些实现定义的hackery吗?当然,恶魔会在其他平台上飞出你的鼻子。
  • @paxdiablo 静态块怎么样?
【解决方案2】:

来自 C++ 标准文档3.6.1 Main Function

程序应包含一个名为 main 的全局函数,它是程序的指定开始。 由实现定义 是否需要独立环境中的程序来定义主函数。

所以,它确实取决于您的编译器/链接器...

【讨论】:

  • 这很有趣。 cpp0x 中没有任何迹象表明第一句仅适用于托管,shall 通常是一个标准词,这意味着它必须如此。所以看起来main 必须存在,即使是独立的。比我有更多 C++-Standard-fu 的人能弄清楚吗?你可以拥有一个main而不需要定义它吗??
  • 实际上 C 似乎也是如此。我正在查看托管部分。独立部分的要求有点宽松。 +1。
  • @paxdiablo:我认为其意图是第二句限定了第一句,因此它的意思是“[在托管环境中]的程序应包含...”我认为“应包含”意思是“应包含[的定义];”我不知道“包含”这个词还能怎么理解。
  • @paxdiablo,我确实参考了 N2461 草案一.. 还没有更新到最近.. 我在这里犯错了吗??
  • 不,我不认为你在这里犯了一个错误,liaK,C99 中有类似的语言。
【解决方案3】:

入口点其实是_start函数(在crt1.o中实现)。

_start 函数准备命令行参数,然后调用main(int argc,char* argv[], char* env[]), 您可以通过设置链接器参数将入口点从_start 更改为mystart

g++ file.o -Wl,-emystart -o runme

当然,这是入口点_start 的替代品,所以你不会得到命令行参数:

void mystart(){

}

请注意,具有构造函数或析构函数的全局/静态变量必须在应用程序开始时初始化并在结束时销毁。如果您打算绕过自动执行的默认入口点,请记住这一点。

【讨论】:

    【解决方案4】:

    如果你在 VS2010 上,this 可以给你一些想法

    这很容易理解,这不是 C++ 标准强制要求的,属于“实现特定行为”的范畴。

    【讨论】:

      【解决方案5】:

      这是高度推测性的,但您可能有一个静态初始化程序而不是 main:

      #include <iostream>
      
      int mymain()
      {
          std::cout << "mymain";
          exit(0);
      }
      
      static int sRetVal = mymain();
      
      int main()
      {
          std::cout << "never get here";
      }
      

      您甚至可以通过将这些东西放入构造函数中来使其“类似于 Java”:

      #include <iostream>
      
      class MyApplication
      {
      public:
          MyApplication()
          {
              std::cout << "mymain";
              exit(0);
          }
      };
      
      static MyApplication sMyApplication;
      
      int main()
      {
          std::cout << "never get here";
      }
      

      现在。面试官可能已经考虑过这些,但我个人从未使用过它们。原因是:

      • 这是非常规的。人们不会理解它,找到切入点并非易事。
      • 静态初始化顺序是不确定的。放入另一个静态变量,如果它被初始化,你将永远不会。

      也就是说,我已经看到它被用于生产环境,而不是 init() 用于库初始化程序。需要注意的是,在 Windows 上,(根据经验)DLL 中的静态变量可能会也可能不会根据使用情况进行初始化。

      【讨论】:

        【解决方案6】:

        修改实际调用main()函数的crt对象,或者提供自己的(不要忘记禁用普通的链接)。

        【讨论】:

        • 我该怎么做?
        【解决方案7】:

        使用 gcc,用属性((构造函数))声明函数,gcc 将在包括 main 在内的任何其他代码之前执行此函数。

        【讨论】:

          【解决方案8】:

          对于基于 Solaris 的系统,我找到了this。您可以在我猜的每个平台上使用.init 部分:

             pragma init (function [, function]...)
          

          来源:

          通过添加对 .init 部分的调用,此 pragma 导致在初始化期间(在 main 之前)或在共享模块加载期间调用每个列出的函数。

          【讨论】:

            【解决方案9】:

            很简单:

            您应该知道,当您在 c 中使用常量时,编译器会执行一种“宏”来更改相应值的常量名称。

            只需在代码开头包含一个#define 参数,其中包含启动函数的名称,后跟名称main

            例子:

            #define my_start-up_function (main)
            

            【讨论】:

              【解决方案10】:

              我认为在链接之前从对象中删除不需要的 main() 符号很容易。

              不幸的是,g++ 的入口点选项对我不起作用(二进制文件在进入入口点之前崩溃)。所以我从目标文件中删除了不需要的入口点。

              假设我们有两个包含入口点函数的源。

              1. target.c 包含我们不想要的 main()。
              2. our_code.c 包含我们希望作为入口点的 testmain()。

              编译后(g++ -c 选项)我们可以得到以下目标文件。

              1. target.o,其中包含我们不想要的 main()。
              2. our_code.o 包含我们希望作为入口点的 testmain()。

              所以我们可以使用 objcopy 去除不需要的 main() 函数。

              objcopy --strip-symbol=main target.o

              我们也可以使用 objcopy 将 testmain() 重新定义为 main()。

              objcopy --redefine-sym testmain=main our_code.o

              然后我们可以将它们都链接成二进制文件。

              g++ target.o our_code.o -o our_binary.bin

              这对我有用。现在当我们运行our_binary.bin 时,入口点是our_code.o:main() 符号,它指的是our_code.c::testmain() 函数。

              【讨论】:

                【解决方案11】:

                在 Windows 上,还有另一种(相当非正统的)方法可以更改程序的入口点:TLS。更多解释请参见:http://isc.sans.edu/diary.html?storyid=6655

                【讨论】:

                  【解决方案12】:

                  是的, 我们可以将主函数名称更改为任何其他名称,例如。 Start、bob、rem 等。

                  编译器如何知道它必须在整个代码中搜索 main() ?

                  编程中没有什么是自动的。 有人做了一些工作让它看起来对我们来说是自动的。

                  所以它已经在启动文件中定义了编译器应该搜索 main()。

                  我们可以将名称 main 更改为其他名称,例如。 Bob 然后编译器将只搜索 Bob()。

                  【讨论】:

                    【解决方案13】:

                    更改链接器设置中的值将覆盖入口点。即,MFC 应用程序使用值“Windows (/SUBSYSTEM:WINDOWS)”将入口点从 main() 更改为 CWinApp::WinMain()。

                    Right clicking on solution > Properties > Linker > System > Subsystem > Windows (/SUBSYSTEM:WINDOWS)
                    

                    ...

                    修改入口点非常实用:

                    MFC 是我们利用 C++ 编写 Windows 应用程序的框架。我知道它很古老,但我的公司出于遗留原因维护了一个!您不会在 MFC 代码中找到 main()。 MSDN 说入口点是 WinMain(),而不是。因此,您可以覆盖基本 CWinApp 对象的 WinMain()。或者,大多数人会覆盖 CWinApp::InitInstance(),因为基础 WinMain() 会调用它。

                    免责声明:我使用空括号来表示一个方法,而不关心有多少参数。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-02-21
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2023-01-30
                      • 1970-01-01
                      相关资源
                      最近更新 更多