在 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++) 中找到应用程序的入口点