Cで以下を達成するための最良の方法は何ですか?
#include <stdio.h>
struct A
{
int x;
};
struct A createA(int x)
{
struct A a;
a.x = x;
return a;
}
struct A a = createA(42);
int main(int argc, char** argv)
{
printf("%d\n", a.x);
return 0;
}
上記のコードをコンパイルしようとすると、コンパイラーから次のエラーが報告されます。
「初期化子要素は定数ではありません」
悪い行はこれです:
struct A a = createA(42);
誰かが何が悪いのか説明できますか? Cの経験はあまりありません。ありがとうございます。
静的初期化を使用しないのはなぜですか?
struct A a = { 42 };
struct A a = { .x = 42 };
その他のメンバー:
struct Y {
int r;
int s;
int t;
};
struct Y y = { .r = 1, .s = 2, .t = 3 };
あなたもすることができます
struct Y y = { 1, 2, 3 };
労働組合にも同じことが機能し、すべてのメンバーを含める必要はなく、メンバーを正しい順序に並べる必要もありません。
ここでの問題は、Cのグローバル/ファイル静的変数がコンパイル時に既知の値を持っている必要があることです。つまり、ユーザー定義関数を使用して値を初期化することはできません。定数式でなければなりません
そのような静的な初期化で関数を呼び出すことはできません。あなたの例では、あなたは単に使うことができます:
struct A a = {42};
より複雑な設定をしている場合は、ライブラリのユーザーに呼び出しを強制するライブラリ構築およびライブラリ破棄関数を提供する必要があります(移植可能にする場合)、またはC++を使用してコンストラクターを利用する必要があります。/destructors、または非標準で移植できない__attribute __((constructor))を利用して、起動時に実行される関数を作成して初期化する必要があります。
より複雑な設定がある場合は、C++を使用することを強くお勧めします。
クラスA { A(){ //コンストラクタで初期化を行うことができます } // .. 。 }; A a;
ただし、純粋なCを使用する必要がある場合は、次のような移植可能な方法を使用します。
typedef void * mylibrary_attr_t; typedef void * mylibrary_t; #ifdef __cplusplus #define EXTERNC extern "C" #else #define EXTERNC #endif EXTERNC int mylibrary_attr_init(mylibrary_attr_t *); EXTERNC int mylibrary_attr_setparam1(mylibrary_attr_t、int); .____。] EXTERNC int mylibrary_attr_setparam2(mylibrary_attr_t、double); // ..ライブラリで使用されるさまざまな属性用の関数 EXTERNC void mylibrary_attr_destroy(mylibrary_attr_t *); EXTERNC int mylibrary_init(mylibrary_t *、mylibrary_attr_t); EXTERNC void mylibrary_destroy(mylibrary_t *); // mylibrary_t //を使用する関数。 ..
基本的に、上記では、mylibrary_init
でライブラリを初期化し、mylibrary_destroy
を使用してライブラリを破棄します。ライブラリを使用する関数ではmylibrary_t
の初期化されたインスタンスが必要になるため、メイン関数を作成した人がmylibrary_init
を呼び出す必要があります。また、初期化関数を、デフォルトで0またはNULLに置き換えることができる「属性」パラメーターに依存させることも有効です。そうすれば、ライブラリを拡張して構成オプションを受け入れる必要がある場合に、ライブラリを利用できます。ただし、これは技術的なアプローチというよりデザインです。
MSVCも使用する好奇心の強いユーザー向け:
Cでは、C++で可能なのと同じように、メインの前に初期化関数を実行することができます(もちろん、Cでそれができなかった場合、C++はそれをどのように実行しますか)。ランタイムライブラリの仕組みをご覧ください。
短い話:
#pragma section(".CRT$XIU",long,read)
int
init_func ()
{
// initialization
return 0; // return 0 is mandatory
}
__declspec(allocate(".CRT$XIU"))
int (*global_initializer)() = init_func;
したがって、C++ほどコンパクトなソーステキストではありませんが、実行できます。また、使用する前に、まずPE形式を理解することをお勧めします。次に、MSVCインストールディレクトリでcrt\src\crt0.cおよびcrt\src\crt0dat.c(両方のファイルで_cinitを検索)を読んで、何が起こっているかを理解します。
関数を初期化子として呼び出すことはできません。 main内で呼び出す必要があります。