CまたはC++プログラムのエントリポイントをmain()
から他の関数に変更するようにインタビューの質問をされました。どのように可能ですか?
標準の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にこれをするのですか?
エントリポイントは、実際には__start
_関数(crt1.oで実装)です。
__start
_関数はコマンドライン引数を準備してmain(int argc,char* argv[], char* env[])
を呼び出します。リンカーパラメーターを設定することで、エントリポイントを__start
_からmystart
に変更できます。
_g++ file.o -Wl,-emystart -o runme
_
もちろん、これはエントリポイント__start
_の置き換えなので、コマンドライン引数を取得しません。
_void mystart(){
}
_
コンストラクターまたはデストラクターを持つグローバル/静的変数は、アプリケーションの最初に初期化し、最後に破棄する必要があることに注意してください。デフォルトのエントリポイントを自動的にバイパスすることを計画している場合は、そのことを覚えておいてください。
C++標準ドキュメント3.6.1 Main Functionから、
プログラムには、プログラムの指定された開始点であるmainと呼ばれるグローバル関数が含まれます。 それは実装定義です自立環境のプログラムがメイン関数を定義する必要があるかどうか。
したがって、それは依存しますコンパイラ/リンカーに依存します...
VS2010を使用している場合、 this はいくつかのアイデアを与える可能性があります
理解しやすいので、これはC++標準では必須ではなく、「実装固有の動作」の領域に入ります。
これは非常に投機的ですが、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の静的情報が使用状況に基づいて初期化される場合とされない場合があります)です。
Gccでは、attribute((constructor))を使用して関数を宣言すると、gccはmainを含む他のコードの前にこの関数を実行します。
実際にmain()
関数を呼び出すcrtオブジェクトを変更するか、独自に提供します(通常のリンクを無効にすることを忘れないでください)。
Solarisベースのシステムの場合、 this が見つかりました。 .init
私が推測するすべてのプラットフォームのセクション:
pragma init (function [, function]...)
ソース:
このプラグマは、.initセクションへの呼び出しを追加することにより、初期化中(メインの前)または共有モジュールのロード中に、リストされた各関数を呼び出します。
とても簡単です:
Cで定数を使用するときに知っておく必要があるように、コンパイラは、それぞれの値の定数の名前を変更する一種の「マクロ」を実行します。
#define
引数をコードの先頭に置き、起動関数の名前の後にmain
という名前を続けます。
例:
#define my_start-up_function (main)
リンクする前に、不要なmain()シンボルをオブジェクトから削除するのは簡単だと思います。
残念ながら、g ++のエントリポイントオプションは機能しません(バイナリはエントリポイントに入る前にクラッシュします)。したがって、オブジェクトファイルから不要なエントリポイントを削除します。
エントリポイント関数を含む2つのソースがあるとします。
コンパイル後(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()
関数を参照します。
Windowsでは、プログラムのエントリポイントを変更する別の(むしろ正統でない)方法があります:TLS
。詳細については、こちらをご覧ください: http://isc.sans.edu/diary.html?storyid=6655
リンカー設定の値を変更すると、エントリポイントが上書きされます。つまり、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()をオーバーライドします。
免責事項:引数の数を気にすることなく、メソッドを示すために空の括弧を使用しています。
はい、メイン関数名を他の名前に変更できます。スタート、ボブ、レムなど.
コンパイラは、コード全体でmain()を検索する必要があることをどのようにして知っていますか?
プログラミングでは何も自動的には行われません。誰かが自動で見えるようにいくつかの作業を行いました。
したがって、コンパイラがmain()を検索する必要があることは、起動ファイルで定義されています。
mainの名前を他の名前に変更できます。 BobとコンパイラーはBob()のみを検索します。