8

私は Microsoft の CRT ソース コードを読んでおり、main() ルーチンの前に関数 __initstdio1 が実行される次のコードを思いつくことができます。

問題は、VC (VC++ コードではなく) で main() ルーチンに入る前にコードを実行する方法です。

#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!

ただし、コードを理解できません。

.CRT$XIC でいくつかのグーグルを実行しましたが、運がありません。専門家が上記のコード セグメント、特に以下について説明してくれませんか。

  1. この行_CRTALLOC(".CRT$XIC") static pinit = __initstdio1;はどういう意味ですか? 変数 pinit の意味は何ですか?
  2. コンパイル中に、コンパイラ (cl.exe) は次のような警告をスローします。

Microsoft (R) 32 ビット C/C++ 最適化コンパイラ バージョン 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. 全著作権所有。

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

警告メッセージを削除するには、どのような修正アクションを実行する必要がありますか?

前もって感謝します。


追加した:

コードを変更し、_PIFV として pinit に型を指定しました。これで、警告メッセージはなくなりました。

新しいコードは次のとおりです。

#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;
}
4

6 に答える 6

5

これを行う簡単な方法。

#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;
}
于 2009-04-08T08:26:54.980 に答える
4

これは、_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の初期化について説明しました。

http://msdn.microsoft.com/en-us/library/bb918180.aspx

于 2009-04-08T08:05:04.220 に答える
2

これについては、少し前に CodeGuru で受賞歴のある記事を書きました。

于 2010-03-11T20:13:29.723 に答える
1

ここにいくつかの情報があります(CRTを検索してください)。変数の重要性はpinitありません。実行可能ファイルに配置されたデータの一部であり、ランタイムがそれを見つけることができます。ただし、次のようなタイプを指定することをお勧めします。

_CRTALLOC(".CRT$XIC") static void (*pinit)()=...

リンカの警告は、おそらくreturn型の関数があることを警告するだけですが、int何も返しません(おそらく、return型をに変更したほうがよいでしょうvoid)。

于 2009-04-08T08:09:24.023 に答える
1

main()C でも、コマンド ラインを C の呼び出し規約に変換するためだけに、入力する前に何らかのコードを実行する必要があります。実際には、標準ライブラリにはいくつかの初期化が必要であり、正確なニーズはコンパイルごとに異なる場合があります。

真のプログラム エントリ ポイントはリンク時に設定され、通常はcrt0歴史的な理由から のような名前のモジュールにあります。おわかりのように、そのモジュールのソースは crt ソースにあります。

リンク時に検出される初期化をサポートするために、特別なセグメントが使用されます。その構造は、初期段階で反復されcrt0、各関数が呼び出される固定シグネチャの関数ポインターのリストです。関数ポインターのこの同じ配列 (またはそれに非常によく似た配列) は、グローバル オブジェクトのコンストラクターへのポインターを保持するために C++ リンクで使用されます。

配列は、リンクされたすべてのモジュールにデータを含めることができるようにすることで、リンカによって埋められます。これらのデータはすべて連結されて、完成した実行可能ファイルのセグメントを形成します。変数の唯一の意味pinitは、そのセグメントに配置されるように (マクロによって) 宣言され_CRTALLOC()、C の起動時に呼び出される関数のアドレスに初期化されることです。

明らかに、これの詳細はプラットフォーム固有です。一般的なプログラミングの場合、初期化と現在のメインを new 内にラップすることで、おそらくより適切に機能します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必要に応じてメモリ ハードウェアを初期化するように変更する以外に良い解決策はないかもしれません。

于 2009-04-08T20:10:41.730 に答える