web-dev-qa-db-ja.com

Cの構造体でconstを初期化する方法(mallocを使用)

私が試してみました;

void *malloc(unsigned int);
struct deneme {
    const int a = 15;
    const int b = 16;
};

int main(int argc, const char *argv[])
{
    struct deneme *mydeneme = malloc(sizeof(struct deneme));
    return 0;
}

そして、これはコンパイラのエラーです:

gereksiz.c:3:17: error: expected ':', ',', ';', '}' or '__attribute__' before '=' token

そして、これも。

void *malloc(unsigned int);
struct deneme {
    const int a;
    const int b;
};

int main(int argc, const char *argv[])
{
    struct deneme *mydeneme = malloc(sizeof(struct deneme));
    mydeneme->a = 15;
    mydeneme->b = 20;
    return 0;
}

そして、これはコンパイラのエラーです:

gereksiz.c:10:5: error: assignment of read-only member 'a'
gereksiz.c:11:5: error: assignment of read-only member 'b'

そしてどちらもコンパイルされませんでした。 mallocでメモリを割り当てるときに、構造体内のconst変数を初期化する方法はありますか?

21
yasar

Mallocされた構造体のフィールドを初期化するには、constをキャストする必要があります。

struct deneme *mydeneme = malloc(sizeof(struct deneme));
*(int *)&mydeneme->a = 15;
*(int *)&mydeneme->b = 20;

あるいは、構造体の初期化されたバージョンを作成し、それをmemcpyすることができます。

struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));

これを頻繁に実行する場合は、deneme_initを静的またはグローバル、あるいはその両方にすることができます(したがって、一度構築するだけで済みます)。


C11標準参照を使用して、コメントの一部で提案されているように、このコードが未定義の動作ではない理由の説明:

  • mallocによって返されるスペースは「const修飾型のオブジェクトdefined」ではないため、このコードは6.7.3/6に違反していません。式mydeneme->aはオブジェクトではなく、式です。 const- qualified型ですが、const修飾型で定義されていない(実際には、どの型でも定義されていない)オブジェクトを示します。

  • 有効なタイプ(6.5/6)は書き込みごとに更新されるため、mallocによって割り当てられたスペースに書き込むことにより、厳密なエイリアスのルールに違反することはありません。

(ただし、mallocによって割り当てられたスペースから読み取ると、厳密なエイリアシングルールに違反する可能性があります)。

Chrisのコードサンプルでは、​​最初のコードは整数値の有効な型をintに設定し、2番目のコードは有効な型をconst intに設定しますが、どちらの場合も*mydenemeを通じてこれらの値を読み取ります厳密なエイリアシングルール(6.5/7箇条書き2)では、オブジェクトの有効なタイプと同等またはそれ以上に修飾された式を介してオブジェクトを読み取ることが許可されているため、正しいです。式mydeneme->aはタイプconst intを持っているので、有効なタイプintおよびconst intのオブジェクトを読み取るために使用できます。

27
Chris Dodd

このようにしようとしましたか:

int main(int argc, const char *argv[])
{
    struct deneme mydeneme = { 15, 20 };
    struct deneme *pmydeneme = malloc(sizeof(struct deneme));
    memcpy(pmydeneme, &mydeneme , sizeof(mydeneme));
    return 0;
}

私はテストしていませんが、コードは正しいようです

10
FBruynbroeck

他の人が言ったように、彼の解決策は標準に従ってUndefined Behaviourを与えると思うので、私はChrist Doddの答えに同意しません。

未定義の動作を呼び出さない方法でconst修飾子を「回避」するには、次の解決策を提案します。

  1. malloc()呼び出しで初期化されたvoid*変数を定義します。
  2. 目的のタイプのオブジェクト(この場合はstruct deneme)を定義して初期化し、const修飾子が文句を言わないように(つまり、宣言行自体で)初期化します。
  3. memcpy()を使用して、struct denemeオブジェクトのビットをvoid*オブジェクトにコピーします。
  4. struct denemeオブジェクトへのポインタを宣言し、以前に(void*)にキャストした(struct deneme *)変数に初期化します。

したがって、私のコードは次のようになります:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct deneme {
    const int a;
    const int b;
};
struct deneme* deneme_init(struct deneme data) {
    void *x = malloc(sizeof(struct deneme));
    memcpy(x, &data, sizeof(struct deneme));
    return (struct deneme*) x;
}
int main(void) {
    struct deneme *obj = deneme_init((struct deneme) { 15, 20, } );
    printf("obj->a: %d, obj->b: %d.\n", obj->a, obj->b);
    return 0;
}
2
pablo1977

興味深いことに、このC99の方法はclangでは機能しますが、gccでは機能しません。

int main(int argc, const char *argv[])
{
    struct deneme *pmydeneme = malloc(sizeof(struct deneme));
    *pmydeneme = (struct deneme) {15, 20};
    return 0;
}
1
Andrey