1

構造体へのポインターの配列に情報を格納しています。つまり、配列の各要素は、リンクされたリストへのポインターです。

配列の長さが分からないので、main() 関数で配列を初期化する代わりに、ダブル ポインターを初期化します。

struct graph** graph_array;

次に、配列の長さを取得したら、関数 GraphInitialize を使用して、graph_array の各要素を初期化しようとします。

int GraphInitialize(struct graph* *graph_array,int vertices)
{ 
  struct graph* graph_array2[vertices+1];
  graph_array = graph_array2;
  int i;

  for (i=0;i<vertices+1;i++)
  {
    graph_array[i] = NULL;
  }

  return 0;
}

しかし、何らかの理由で、これは更新されたgraph_arrayをmain()に返していません。基本的に、この関数は graph_array をローカルで更新しており、変更は行われていません。その結果、graph_array の要素にアクセスしようとすると、初期化されていないため、エラーが発生します。私は何を間違っていますか?

編集: Tom Ahh との対話に続いて、これをさらに混乱させる何かを追加する必要があります。

main() から直接 GraphIntialize を呼び出しません。代わりに、以下に示すように、main から getdata() を呼び出し、graph_array へのポインターを getdata に渡します。

    getdata(argc, argv, vertpt, edgept, &graph_array)

    int getdata(int argc, char *argv[], int *verts, int *edges, struct graph* **graph_array)

次に、getdata は入力ファイルから頂点の数を取得し、それを使用して GraphInitialize を呼び出します。

    if ((GraphInitialize(&graph_array, *verts)) == -1)
    {
      printf("GraphCreate failed");
      return 0;
    }

これにより、次のエラーが発生します。

4

4 に答える 4

4

に何かを割り当てるときはgraph_array、単純にそのローカル コピーに割り当てます。関数内で行われた変更は、呼び出し元には表示されません。値を変更できるようにするには、ポインター値で渡す必要があります。関数プロトタイプを に変更し、int GraphInitialize(struct graph ***graph_array,int vertices)呼び出すときに を使用しますGraphInitialize(&graph_array, 42)

コードの 2 番目の問題は、 を作成するときに、それを関数graph_array2に対してローカルに宣言することGraphInitialize()です。したがって、関数を終了すると、graph_array2 を に割り当てたとしても破棄され*graph_arrayます。(スターはポインターを逆参照して、ポインターが指す値に割り当てます)。

に割り当てを変更*graph_array = malloc(sizeof(*graph_array) * vertices);すれば、問題ないはずです。

于 2012-11-28T01:15:36.820 に答える
2

メモリは、スタックとヒープの 2 つの部分に分けられます。Malloc はヒープからメモリのチャンクを返します。ヒープは関数間で存続しますが、解放する必要があります。したがって、プログラムは malloced() メモリを追跡し、その上で free() を呼び出すように注意する必要があります。

変数 graph_array2[vertices+1] を宣言すると、ローカル変数がスタックに割り当てられます。関数が戻ると、スタック ポインターがポップされ、関数呼び出しで割り当てられたメモリが "解放" されます。メモリを手動で管理する必要はありませんが、関数呼び出しが終了するとメモリは存在しなくなります。

2 つの割り当てスタイルの説明については、 http ://www.ucosoft.com/stack-vs-heap.html を参照してください。

于 2012-11-28T01:22:41.230 に答える
1

C99スタイルのローカル配列割り当てを使用しています。関数が戻ると、配列は消えます。malloc()代わりに、関数の後で持続するメモリを割り当てるためにを使用する必要があります。typedefsコードを読みやすくするために使用できます。

typedef struct graph_node_s {  // linked list nodes
  struct graph_node_s *next;
   ...
} GRAPH_NODE;

typedef GRAPH_NODE *NODE_REF;  // reference to node
typedef NODE_REF *GRAPH;       // var length array of reference to node

GRAPH AllocateGraph(int n_vertices)
{
  int i;
  GRAPH g;

  g = malloc(n_vertices * sizeof(NODE_REF));
  if (!g) 
    return NULL;
  for (i = 0; i < n_vertices; i++)
    g[i] = NULL;
  return g;
}
于 2012-11-28T01:26:28.603 に答える
1

2 つの問題があります。

まず、autoextentgraph_array2を持ちます。これは、関数の本体である外側のスコープ内にのみ存在することを意味します。関数が終了すると、そのメモリは解放され、意味のある場所を指しなくなります。 GraphInitializegraph_array

第 2 に、パラメーターへの変更graph_arrayは関数に対してローカルです。変更は呼び出し元に反映されません。すべてのパラメータは値で渡されることに注意してください。関数にポインターを渡し、ポインターの値を関数によって変更する場合は、次のようにポインターにポインターを渡す必要があります。

void foo(int **p)
{
  *p = some_new_pointer_value();
  return;
}

int main(void)
{
  int *ptr = NULL;
  foo(&ptr);
  ...
}

配列にメモリを割り当てる場合InitializeGraphは、次のようにする必要があります。

int InitializeGraph(struct graph ***graph_array, int vertices)
{
  *graph_array = malloc(sizeof **graph_array * vertices);
  if (*graph_array)
  {
    int i;
    for (i = 0; i < vertices; i++)
    {
      (*graph_array}[i] = NULL; // parentheses matter here!
    }
  }
  else
  {
    return -1;
  }

  return 0;
}

int main(void)
{
  int v;
  struct graph **arr;
  ...
  if (GraphInitialize(&arr, v) == 0)
  {
    // array has been allocated and initialized.
  }
  ...
}

のような後置演算子は、の[]ような単項演算子よりも優先順位が高い*ため、式は;*arr[i]として解釈されます。配列の '番目の要素を*(arr[i])逆参照しています。iでは、添字を付けるGraphInitializeに逆参照する必要がある(は配列ではなく、配列を指している) ため、 と記述する必要があります。 graph_array graph_array(*graph_array)[i]

于 2012-11-28T01:44:33.750 に答える