web-dev-qa-db-ja.com

C ++のメイン関数では、他の関数に変更する方法をプログラムするためのエントリポイントですか?

CまたはC++プログラムのエントリポイントをmain()から他の関数​​に変更するようにインタビューの質問をされました。どのように可能ですか?

31
Badr

標準のC(そして、おそらくC++も同様です)では、少なくともホスト環境ではできません(ただし、以下を参照)。この規格では、Cコードの開始点はmainであると規定されています。標準(c99)は、議論の余地をあまり残していません。

5.1.2.2.1プログラムの起動:(1)プログラムの起動時に呼び出される関数の名前はmainです。

それでおしまい。次に、パラメーターと戻り値について少し説明しますが、名前を変更する余地はありません。

これは、ホストされた環境用です。この規格では、独立した環境も可能です(つまり、組み込みシステムなどの場合はOSなし)。独立した環境の場合:

独立した環境(オペレーティングシステムの利点なしにCプログラムの実行が行われる可能性がある)では、プログラムの起動時に呼び出される関数の名前と型は実装定義です。 4節で必要とされる最小セット以外の、独立型プログラムで使用可能なライブラリ機能は、実装定義です。

Cで "trickery"を使用できますimplementationsにすると、mainがエントリポイントではないように見せることができます。これは実際、初期のWindowsコンパイラがWinMainを開始点としてマークするために行ったものです。


最初の方法:リンカーはstart.oのようなファイルにいくつかのメインの起動前のコードを含めることができ、C環境をセットアップしてmainを呼び出すのはこのコードの一部です。代わりにbobを呼び出すものに置き換えるのは止められません。


2番目の方法:一部のリンカーは、コマンドラインスイッチにそのオプションを提供しているため、スタートアップコードを再コンパイルせずに変更できます。


3番目の方法:次のコードにリンクできます。

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

yourコードのエントリポイントは、bobではなくmainのようです。


しかし、これらすべては、おそらく学術的に興味があるものの、何十年にもわたるカットコードの中で、これが必要または望ましい、単一の孤独な状況を考えることができないという事実を変えることはありません。

私はインタビュアーに尋ねます:なぜあなたはwantにこれをするのですか?

45
paxdiablo

エントリポイントは、実際には__start_関数(crt1.oで実装)です。

__start_関数はコマンドライン引数を準備してmain(int argc,char* argv[], char* env[])を呼び出します。リンカーパラメーターを設定することで、エントリポイントを__start_からmystartに変更できます。

_g++ file.o -Wl,-emystart -o runme
_

もちろん、これはエントリポイント__start_の置き換えなので、コマンドライン引数を取得しません。

_void mystart(){

}
_

コンストラクターまたはデストラクターを持つグローバル/静的変数は、アプリケーションの最初に初期化し、最後に破棄する必要があることに注意してください。デフォルトのエントリポイントを自動的にバイパスすることを計画している場合は、そのことを覚えておいてください。

11
Lefteris E

C++標準ドキュメント3.6.1 Main Functionから、

プログラムには、プログラムの指定された開始点であるmainと呼ばれるグローバル関数が含まれます。 それは実装定義です自立環境のプログラムがメイン関数を定義する必要があるかどうか。

したがって、それは依存しますコンパイラ/リンカーに依存します...

10
liaK

VS2010を使用している場合、 this はいくつかのアイデアを与える可能性があります

理解しやすいので、これはC++標準では必須ではなく、「実装固有の動作」の領域に入ります。

7
Chubsdad

これは非常に投機的ですが、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の静的情報が使用状況に基づいて初期化される場合とされない場合があります)です。

4
lorro

Gccでは、attribute((constructor))を使用して関数を宣言すると、gccはmainを含む他のコードの前にこの関数を実行します。

3
Galaxy

実際にmain()関数を呼び出すcrtオブジェクトを変更するか、独自に提供します(通常のリンクを無効にすることを忘れないでください)。

Solarisベースのシステムの場合、 this が見つかりました。 .init私が推測するすべてのプラットフォームのセクション:

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

ソース:

このプラグマは、.initセクションへの呼び出しを追加することにより、初期化中(メインの前)または共有モジュールのロード中に、リストされた各関数を呼び出します。

2
Amir Zadeh

とても簡単です:

Cで定数を使用するときに知っておく必要があるように、コンパイラは、それぞれの値の定数の名前を変更する一種の「マクロ」を実行します。

#define引数をコードの先頭に置き、起動関数の名前の後にmainという名前を続けます。

例:

#define my_start-up_function (main)
2
Paulo Rossi

リンクする前に、不要なmain()シンボルをオブジェクトから削除するのは簡単だと思います。

残念ながら、g ++のエントリポイントオプションは機能しません(バイナリはエントリポイントに入る前にクラッシュします)。したがって、オブジェクトファイルから不要なエントリポイントを削除します。

エントリポイント関数を含む2つのソースがあるとします。

  1. target.cには、不要なmain()が含まれています。
  2. our_code.cには、エントリーポイントにしたいtestmain()が含まれています。

コンパイル後(g ++ -cオプション)、次のオブジェクトファイルを取得できます。

  1. target.o、これには不要なmain()が含まれています。
  2. エントリポイントにしたいtestmain()を含むour_code.o.

したがって、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()関数を参照します。

2
shuva

Windowsでは、プログラムのエントリポイントを変更する別の(むしろ正統でない)方法があります:TLS。詳細については、こちらをご覧ください: http://isc.sans.edu/diary.html?storyid=6655

1
ruslik

リンカー設定の値を変更すると、エントリポイントが上書きされます。つまり、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()をオーバーライドできます。または、基本のWinMain()がCWinApp :: InitInstance()を呼び出すため、ほとんどの人がCWinApp :: InitInstance()をオーバーライドします。

免責事項:引数の数を気にすることなく、メソッドを示すために空の括弧を使用しています。

0
John Doe

はい、メイン関数名を他の名前に変更できます。スタート、ボブ、レムなど.

コンパイラは、コード全体でmain()を検索する必要があることをどのようにして知っていますか?

プログラミングでは何も自動的には行われません。誰かが自動で見えるようにいくつかの作業を行いました。

したがって、コンパイラがmain()を検索する必要があることは、起動ファイルで定義されています。

mainの名前を他の名前に変更できます。 BobとコンパイラーはBob()のみを検索します。

0
Gaurav Pal