我被问到一个面试问题,将C或C ++程序的入口点从main()
更改为任何其他功能。怎么可能?
答案 0 :(得分:43)
在标准C中(我相信,C ++也是如此),你不能,至少不能用于托管环境(但见下文)。该标准指定C代码的起始点为main
。标准(c99)没有为论证留下太多空间:
5.1.2.2.1程序启动:(1)程序启动时调用的函数名为main。
就是这样。然后它对参数和返回值略有不解之处,但是更改名称确实没有余地。
这适用于托管环境。该标准还允许独立环境(即,没有OS,用于诸如嵌入式系统之类的东西)。对于独立环境:
在独立环境中(可以在没有操作系统任何好处的情况下执行C程序),在程序启动时调用的函数的名称和类型是实现定义的。除了第4章要求的最小集之外,任何独立程序可用的库设施都是实现定义的。
您可以在C 实现中使用“trickery”,这样您就可以看起来main
不是入口点。事实上,这是早期Windows编制者将WinMain
标记为起点的原因。
第一种方式:链接器可能在start.o
这样的文件中包含一些主要的启动代码,正是这段代码运行以设置C环境然后调用main
。没有什么可以阻止你用调用bob
的东西替换它。
第二种方式:一些链接器通过命令行开关提供了这个选项,这样您就可以在不重新编译启动代码的情况下进行更改。
第三种方式:你可以链接这段代码:
int main (int c, char *v[]) { return bob (c, v); }
然后您的 代码的入口点似乎是bob
而不是main
。
然而,所有这些虽然可能具有学术兴趣,但并没有改变这样一个事实,即在我几十年的切割代码中,我无法想到一个单独的情况,这将是必要或可取的。
我会问面试官:为什么你想这样做?
答案 1 :(得分:10)
入口点实际上是_start
函数(在 crt1.o 中实现)。
_start
函数准备命令行参数,然后调用main(int argc,char* argv[], char* env[])
,
您可以通过设置链接器参数将入口点从_start
更改为mystart
:
g++ file.o -Wl,-emystart -o runme
当然,这是入口点_start
的替代,因此您将无法获得命令行参数:
void mystart(){
}
请注意,具有构造函数或析构函数的全局/静态变量必须在应用程序开始时初始化并在结束时销毁。如果您打算绕过自动执行此操作的默认入口点,请记住这一点。
答案 2 :(得分:9)
来自C ++标准文档 3.6.1主要功能,
程序应包含一个名为main的全局函数,它是程序的指定开始。 是实施定义的 是否需要在独立环境中的程序来定义主要功能。
因此,确实依赖在您的编译器/链接器上......
答案 3 :(得分:7)
如果你在VS2010上,this可以给你一些想法
由于它很容易理解,这不是C ++标准的强制要求,而是属于“实现特定行为”的范畴。
答案 4 :(得分:3)
修改实际调用main()
函数的crt对象,或提供自己的(不要忘记禁用正常链接)。
答案 5 :(得分:3)
使用gcc,使用attribute((constructor))声明函数,gcc将在包括main之类的任何其他代码之前执行此函数。
答案 6 :(得分:3)
这是高度推测的,但你可能有一个静态初始化器而不是main:
int mymain()
{
std::cout << "mymain";
exit(0);
}
static int sRetVal = mymain();
int main()
{
std::cout << "never get here";
}
你甚至可以通过将这些东西放在构造函数中来制作类似Java的&#39;:
#include <iostream>
class MyApplication
{
public:
MyApplication()
{
std::cout << "mymain";
exit(0);
}
};
static MyApplication sMyApplication;
int main()
{
std::cout << "never get here";
}
现在。面试官可能已经考虑过这些,但我个人从不使用它们。原因是:
那就是说,我已经看到它用于生产而不是init()
用于库初始化程序。需要注意的是,在Windows上(根据经验),您在DLL中的静态可能会或可能不会根据使用情况进行初始化。
答案 7 :(得分:2)
对于基于Solaris的系统,我找到了this。您可以在我猜的每个平台上使用.init
部分:
pragma init (function [, function]...)
来源:
此pragma导致在初始化期间(main之前)或共享模块加载期间调用每个列出的函数,方法是添加对.init部分的调用。
答案 8 :(得分:2)
这很简单:
正如您应该知道在c中使用常量时,编译器会执行一种“宏”,更改相应值的常量名称。
只需在代码开头添加一个#define
参数,其名称为启动函数,后跟名称main
:
示例:
#define my_start-up_function (main)
答案 9 :(得分:2)
我认为在链接之前很容易从对象中删除不需要的main()符号。
不幸的是,g ++的入口点选项对我不起作用(二进制文件在进入入口点之前崩溃)。所以我从目标文件中删除了不需要的入口点。
假设我们有两个包含入口点函数的源。
编译完成后(g ++ -c选项),我们可以得到以下目标文件。
因此我们可以使用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()
函数。
答案 10 :(得分:1)
在Windows上,有另一种(相当不正统的)方式来更改程序的入口点:TLS
。有关更多说明,请参阅此处:http://isc.sans.edu/diary.html?storyid=6655
答案 11 :(得分:0)
是, 我们可以将主函数名称更改为任何其他名称,例如。开始,bob,rem等。
编译器如何知道它必须在整个代码中搜索main()?
编程中没有什么是自动的。 有人做了一些工作让我们看起来很自动。
所以在启动文件中已经定义了编译器应该搜索main()。
我们可以将名称main更改为其他任何内容,例如。 Bob然后编译器将仅搜索Bob()。
答案 12 :(得分:0)
在“链接器设置”中更改值将覆盖入口点。也就是说,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()会调用它。
免责声明:我使用空括号来表示一个方法,而不关心有多少个参数。