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

时间:2010-10-20 04:37:48

标签: c++ c main

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

13 个答案:

答案 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 ++的入口点选项对我不起作用(二进制文件在进入入口点之前崩溃)。所以我从目标文件中删除了不需要的入口点。

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

  1. target.c包含我们不想要的main()。
  2. our_code.c包含我们希望成为入口点的testmain()。
  3. 编译完成后(g ++ -c选项),我们可以得到以下目标文件。

    1. target.o,包含我们不想要的main()。
    2. our_code.o包含我们希望成为入口点的testmain()。
    3. 因此我们可以使用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()会调用它。

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