を実行する方法に一部のコードに入る前にmain()ルーチンでVC?
質問
私は読むMicrosoftのCRTソースコードの、将来には、次のコードの機能__initstdio1が実行される前にmain()日常的に見られる。
それでは、どのように実行し一部のコードに入る前にmain()ルーチンにはVC(VC++コード)とは?
#include <stdio.h>
#pragma section(".CRT$XIC",long,read)
int __cdecl __initstdio1(void);
#define _CRTALLOC(x) __declspec(allocate(x))
_CRTALLOC(".CRT$XIC") static pinit = __initstdio1;
int z = 1;
int __cdecl __initstdio1(void) {
z = 10;
return 0;
}
int main(void) {
printf("Some code before main!\n");
printf("z = %d\n", z);
printf("End!\n");
return 0;
}
出力:
Some code before main!
z = 10
End!
しかし、私はついて理解することができます。
いろいろやってみましたgoogleす。CRT$XICな運が見られる。では一部の専門家に説明の上記コードセグメント私は、特に、次の
- このライン
_CRTALLOC(".CRT$XIC") static pinit = __initstdio1;
を意味するのか。この変数pinit? - 中編はコンパイラ(cl.exe)投警告と下記の通りとなります。
Microsoft(R)32-bitのC/C++の最適化コンパイラのバージョン15.00.30729.01のための80x86 Copyright(C)マイクロソフト株式会社All rights reserved.
stdmacro.c
stdmacro.c(9) : warning C4047: 'initializing' : 'int' differs in levels of indirection from 'int (__
cdecl *)(void)'
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:stdmacro.exe
stdmacro.obj
何に是正措置のニーズを検討するに当たって、これを削除、警告メッセージ?
よろしくお願いします。
追加:
修のコードをタイプpinitとして_PIFV.現在の警告メッセージがなくなっています。
の新しいコード:
#include <stdio.h>
#pragma section(".CRT$XIC1",long,read)
int __cdecl __initstdio1(void);
typedef int (__cdecl *_PIFV)(void);
#define _CRTALLOC(x) __declspec(allocate(x))
_CRTALLOC(".CRT$XIC1") static _PIFV pinit1 = __initstdio1;
int z = 1;
int __cdecl __initstdio1(void) {
z = 100;
return 0;
}
int main(void) {
printf("Some code before main!\n");
printf("z = %d\n", z);
printf("End!\n");
return 0;
}
解決
いくつかの情報ここを(CRTの検索)があります。変数pinit
の意義は、それが実行時にそれを見つけることができ、実行可能、に配置されたデータのほんの一部だ、noneです。しかし、私はこのように、それを形を与えることをアドバイスします:
_CRTALLOC(".CRT$XIC") static void (*pinit)()=...
リンカ警告はおそらくあなたが戻り値の型をint
た機能を持って警告しますが、何も返さない(おそらく、あなたはより良いvoid
する戻り値の型を変更すると思います)。
他のヒント
これを実行するための簡単な方法を。
#include <iostream>
int before_main()
{
std::cout << "before main" << std::endl;
return 0;
}
static int n = before_main();
void main(int argc, char* argv[])
{
std::cout << "in main" << std::endl;
}
これは_CRTALLOCは以下のように定義されているものです
extern _CRTALLOC(".CRT$XIA") _PVFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PVFV __xi_z[];// C initializers
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];// C++ initializers
これは、あなたの関数の__initstdio1
へのポインタが置かれているの事前初期化するもの、のテーブルです。
このページで説明CRTの初期化:
C ++では、少なくとも、あなたはすべてのその実装を必要としない特定のもの:
#include <iostream>
struct A {
A() { std::cout << "before main" << std::endl; }
};
A a;
int main() {
std::cout << "in main" << std::endl;
}
私は、受賞歴のある記事を書いたのしばらく前CodeGuru上でこの程度ます。
でもCで、唯一のCの呼び出し規約にコマンドラインを変換する場合main()
は、入力される前に実行されるいくつかのコードが必要です。実際には、標準ライブラリには、いくつかの初期化を必要とし、正確なニーズをコンパイルするには、コンパイルから変えることができます。
真のプログラムのエントリポイントは、リンク時に設定され、歴史的な理由のためcrt0
のようなものをという名前のモジュールに通常です。あなたが見つけてきたように、そのモジュールのソースは、CRTソースで提供されます。
リンク時に発見された初期化をサポートするために、特別なセグメントが使用されています。その構造は、早期crt0
で反復し、各関数が呼び出される固定署名の関数ポインタのリストです。関数ポインタの同じ配列(又は非常にそれのようなもの)は、グローバルオブジェクトのコンストラクタへのポインタを保持するためにC ++リンクで使用されます。
アレイは、すべてのモジュールが全て完成し、実行可能でセグメントを形成するために一緒に連結され、その中にデータを含むように連結させることによって、リンカーによって充填されます。
可変pinit
にのみ意義は、それがそのセグメント内に位置するように(_CRTALLOC()
マクロによって)宣言され、そしてCの起動時に呼び出される関数のアドレスに初期化されることである。
もちろん、これの詳細は非常にプラットフォーム固有です。一般的なプログラミングのために、あなたはおそらく、より良い新しいmain()
の内側にあなたの初期化とあなたの現在のメインのラップによって提供されます:
int main(int argc, char **argv) {
early_init();
init_that_modifies_argv(&argc, &argv);
// other pre-main initializations...
return real_main(argc,argv);
}
特別な目的のために、crt0
モジュール自体を変更またはと呼ばれる追加の初期の初期化関数を取得するには、コンパイラ固有のトリックを行うと、最良の答えすることができます。オペレーティングシステムローダーなしでROMから実行する組み込みシステムを構築する場合、例えば、crt0
するためのパラメータをプッシュする上で、すべてのスタックを持つためにmain()
モジュールの動作をカスタマイズする必要があるのが一般的です。その場合は、あなたのニーズに合わせてメモリのハードウェアを初期化するためにcrt0
を修正するよりも良い解決策があるかもしれません。