web-dev-qa-db-ja.com

構造体ポインターを返す

ポインターを返す次の構造体と関数があるとします。

typedef struct {
  int num;
  void *nums;
  int size;
} Mystruct;

Mystruct *mystruct(int num, int size)
{ 
   //Is the following correct? Is there a more efficient way?
   Mystruct mystruct;
   mystruct.num = num;
   mystruct.size = size;
   mystruct.nums = malloc(num*sizeof(size));
   Mystruct *my;
   *my = mystruct;
   return my;
}

上記の関数を使用してMystructポインターを定義したいと思います。 Mystruct変数を宣言し、Mystructのプロパティを定義し、それにポインターを割り当て、ポインターを返すか、ポインターを介してmystructプロパティのプロパティをすぐに定義する必要がありますか?

22
idealistikz

Mystruct変数を宣言し、Mystructのプロパティを定義し、それにポインターを割り当て、ポインターを返す必要があります

(「auto」ストレージクラスの)関数で定義された変数は、関数が終了すると消え、ダングリングポインターを返すためです。

acceptMystruct(それを割り当てる呼び出し側の責任)へのポインタを入力し、入力することができます。または、mallocを使用して新しいものを作成できます(完了したら呼び出し元が解放する必要があります)。 2番目のオプションでは、少なくとも、熱心な関数シグネチャを保持できます。

Mystruct *mystruct(int num, int size)
{
   Mystruct *p = malloc(sizeof(MyStruct));
   ....
   return p;
}

しかし、それはしばしば劣るものです-呼び出し元はとにかく責任を負わなければならないので、最初のオプションを選択し、潜在的にパフォーマンスを上げる可能性があります(使用範囲が制限されていることを知っているため、呼び出し元が自動クラスインスタンスを使用できる場合) 。

36
Alex Martelli

関数が終了すると変数の割り当てが解除されるため、変数を使用できません。例えば:

Mystruct *mystruct(int num, int size)
{
   MyStruct x;
   x.num = 1;
   ...
   return &x;
}

Xのメモリは、終了するとすぐに割り当てが解除されるため、セグメンテーション違反またはアクセス違反が発生します。そのため、構造体にメモリを割り当てる必要があります(また、後で解放することを確認してください)。後者の例...

Mystruct *mystruct(int num, int size)
{
   MyStruct *x;
   x = (MyStruct*)malloc( sizeof( MyStruct ) );
   x->num = 1;
   ...
   return x;
}
7
staticman

汎用コードを書いていて、それがどのように使用されるかわからない場合は、両方のオプションを提供することをお勧めします。

int mystructm(Mystruct *storage, int num, int size)
{
    int rv = 0;

    storage->num = num;
    storage->size = size;
    storage->nums = malloc(num*sizeof(size));
    if (!storage->nums)
        return -1;

    return 0;
}

Mystruct *mystruct(int num, int size)
{
    Mystruct *mp = (Mystruct *)malloc(sizeof(Mystruct));
    if (mp)
    {
        if (mystructm(mp, num, size) == -1)
        {
            free(mp);
            mp = NULL;
        }
    }

    return mp;
}

ライブラリライターとして、ポリシーを指示する必要はありません(各Mystructを動的に割り当てる必要があるなど)が、アプリケーションライターにそれを決定させる必要があります。

3

新しいMystructを割り当て、それへのポインターを返すことは、通常、次のようになります。

_Mystruct *mystruct(int num, int size)
{
   Mystruct *result;

   result = malloc(sizeof(MyStruct));
   if (!result)
     return NULL;

   result->num = num;
   ...

   return result;
}
_

後で、ここでMystructで割り当てられたmallocを完了したら、free()で再び解放する必要があります。

ローカル変数を宣言して、そのローカル変数へのポインターを返すだけでは機能しません。ローカル変数は、関数の最後でスコープ外になり、保存されたメモリは他の目的で再利用される可能性が高くなります。返されたポインタは、ローカル変数がかつて存在していたメモリ位置を指し示しますが、その変数はもう存在しないため、そのポインタはあまり役に立ちません。

2
sth

ポインタは、構造に割り当てるものではなく、構造として扱うことを希望するメモリ内の位置を示すものであることを覚えておくことが重要です。あなたの質問に基づいて、あなたは本当にデータ構造を保持するためにメモリを割り当てたいです。これにより、割り当てられたメモリ位置へのポインタが得られます。それが得られたら、それを返すことができます。


[〜#〜] edit [〜#〜](元の質問への編集後)質問への編集を見ると、間違いなく「my」ポインタに問題があります。これは初期化されず、メモリ内の任意の場所を指す場合があります。構造をコピーしようとすると、おそらくセグメンテーション違反が発生します。

2
graza

それを行うもう一つの方法..

int mystruct(Mystruct *mystruct, int num, int size){
   if(mystruct == NULL)
      return -1;

   mystruct->num = num;
   mystruct->size = size;
   ::
   return 0;
}

int main(){
   Mystruct my;

   if(mystruct(&my, 3, 4) != 0){
      fprintf(stderr, "Cannot init!\n");
      exit(0);
   }
   ::
}
2
N 1.1