在 C++ 中,主函数是编程的入口点,我如何将其更改为其他函数?

Posted

技术标签:

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

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

【问题讨论】:

你没有。它必须要么是main,要么是一些实现定义的入口点。没有标准的方法。 取决于您的编译器/链接器。 程序的入口点是它在机器代码级别开始执行的地方。这很少,如果有的话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++ 的入口点选项对我不起作用(二进制文件在进入入口点之前崩溃)。所以我从目标文件中删除了不需要的入口点。

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

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

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

    target.o,其中包含我们不想要的 main()。 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() 会调用它。

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

【讨论】:

以上是关于在 C++ 中,主函数是编程的入口点,我如何将其更改为其他函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Visual Studio (C++) 中找到应用程序的入口点

如何在 Python 中遍历 C++ 集?

在函数 C++ 中分配内存二维数组

在 VS (2010) 中使用标准“主”条目构建和运行 C++ 控制台应用程序

C# 无法在未损坏的 C++ 库上找到入口点

我可以更改 Pandas 中均值函数的 dtype 吗?我想将其更改为 int 类型 [关闭]