3

void*型のデータを保持する汎用リンクリストがあります。リストにstructemployee型を入力しようとしていますが、最終的にはオブジェクトstructemployeeも破棄したいと思います。

このジェネリックリンクリストヘッダーファイルを考えてみましょう(私はchar *型でテストしました):

struct accListNode                 //the nodes of a linked-list for any data type
{
  void *data;                     //generic pointer to any data type
  struct accListNode *next;       //the next node in the list
};

struct accList                    //a linked-list consisting of accListNodes
{
  struct accListNode *head;
  struct accListNode *tail;
  int size;
};

void accList_allocate(struct accList *theList);           //allocate the accList and set to NULL
void appendToEnd(void *data, struct accList *theList);    //append data to the end of the accList
void removeData(void *data, struct accList *theList);         //removes data from accList
  --------------------------------------------------------------------------------------

従業員の構造を検討する

struct employee 
{ 
   char name[20]; 
   float wageRate; 
} 

ここで、main()から呼び出されるこのサンプルテストケースについて考えてみます。

    void test2()
    {
      struct accList secondList;
      struct employee *emp = Malloc(sizeof(struct employee));
      emp->name = "Dan";
      emp->wageRate =.5;

      struct employee *emp2 = Malloc(sizeof(struct employee));
      emp2->name = "Stan";
      emp2->wageRate = .3;

      accList_allocate(&secondList);
      appendToEnd(emp, &secondList);
      appendToEnd(emp2, &secondList);

      printf("Employee: %s\n", ((struct employee*)secondList.head->data)->name);   //cast to type struct employee
      printf("Employee2: %s\n", ((struct employee*)secondList.tail->data)->name);  
    }

以下に投稿した回答で問題が解決するのはなぜですか?ポインタとメモリ割り当てに関係があると思います。私が使用する関数Malloc()は、NULLが返されるかどうかをチェックするカスタムmallocです。

これが私の一般的なリンクリスト実装全体へのリンクです:https ://codereview.stackexchange.com/questions/13007/c-linked-list-implementation

4

4 に答える 4

6

問題は、このaccList_allocate()とその使用です。

struct accList secondList;
accList_allocate(&secondList);

元のtest2()では、secondListはスタック上のメモリです。&secondListは、そのメモリへのポインタです。accList_allocate()を呼び出すと、ポインタのコピーがスタックメモリを指すように渡されます。次に、Malloc()はメモリのチャンクを返し、元のsecondListではなく、ポインタのコピーに割り当てます。

戻ってきても、secondListはまだスタック上の初期化されていないメモリを指しているため、appendToEnd()の呼び出しは失敗します。

secondListにジャンクがないことを除いて、同じことが答えにも起こります。おそらく偶然、おそらくコンパイラの設計によるものです。いずれにせよ、それはあなたが頼るべきものではありません。

また:

struct accList *secondList = NULL;

accList_allocate(&secondList);

そして、accList_allocate()を変更します

accList_allocate(struct accList **theList) {
    *theList = Malloc(sizeof(struct accList));
    (*theList)->head = NULL;
    (*theList)->tail = NULL;
    (*theList)->size = 0;
}

また

struct accList secondList;

accList_initialise(secondList);

accList_allocate()が割り当てられないため、accList_initialise()に変更されました

accList_initialise(struct accList *theList) {
    theList->head = NULL;
    theList->tail = NULL;
    theList->size = 0;
}
于 2012-06-26T17:22:47.853 に答える
1

私はあなたの問題はこれだと思います:

  1. 元の関数secondListでスタックに割り当てました。test2
  2. スタックメモリが汚れている可能性があるためsecondList、初期化が必要です
  3. 関数accList_allocateはリストへのポインターを受け取りますが、それをMalloc呼び出しで上書きします。これは、渡したポインタが初期化されないことを意味します。
  4. test2実行しようとすると、(メモリが初期化されていないため)不正なポインタにヒットします。

割り当てたときに機能する理由mainは、プログラムの起動時にCコンパイラがスタックをゼロにする可能性があるためです。スタックに変数を割り当てる場合main、その割り当ては(プログラムが終了するまで)永続的であるため、secondListに割り当てるときに実際に、そして偶然に適切に初期化されますmain

現在accList_allocateのコードは、渡されたポインターを実際には初期化しません。また、コードの残りの部分は、で割り当てられたポインターを認識しませんMalloc。あなたの問題を解決するために、私は新しい関数を作成します:accList_initializeその唯一の仕事はリストを初期化することです:

void accList_initialize(struct accList* theList)
{
    // NO malloc
   theList->head = NULL;
   theList->tail = NULL;
   theList->size = 0;
}

accList_allocate元の関数ではなく、これを使用してくださいtest2。本当にヒープにリストを割り当てたい場合は、そうする必要があります(スタックに割り当てられた構造体と混合しないでください)。割り当てられた構造体へのポインタをaccList_allocate 返します。

struct accList* accList_allocate(void)
{
   struct accList* theList = Malloc( sizeof(struct accList) );
   accList_initialize(theList);
   return theList;
}
于 2012-06-26T17:07:16.653 に答える
0

従業員の構造がどのように設計されているかによって異なりますが、注意が必要です。

strcpy(emp->name, "Dan");

emp->name = "Dan";

機能が異なります。特に、後者はバスエラーの原因となる可能性があります。これは、通常、この方法で文字列リテラルに書き込むことができないためです。特にあなたのコードが次のようなものを持っている場合

name ="NONE"

など。

編集:さて、従業員の構造体の設計では、問題はこれです:

配列に割り当てることはできません。C標準には変更可能な左辺値のリストが含まれており、配列はそれらの1つではありません。

char name[20];
name = "JAMES" //illegal

strcpyは問題ありません。name[0]によって逆参照されたメモリアドレスに移動し、「JAMES\0」を一度に1バイトずつメモリにコピーします。

于 2012-06-26T16:18:31.900 に答える
0

上記の質問で、元のコードに基づいてここで間違っていると思われる2つのこと

あなたが見たのは未定義の振る舞いであり、それから変数に文字列リテラルを割り当てていたときのバスエラーメッセージが発生しました。実際にはstrcpy関数を使用する必要があったのに、元のコードをそれに応じて編集しました。将来的に覚えておくべきこと:)

単語の使用はMalloc混乱を引き起こします、特に査読では、査読者は頭がおならをして、「おっと、これは何ですか、それはmallocであるべきではありませんか?」と言うでしょう。そしておそらくそれを上げます。(基本的に、C標準ライブラリ関数と同じような名前のカスタム関数を呼び出さないでください)

あなたはをチェックしていませんNULL、もしあなたのスープバージョンがMalloc失敗したらどうなるでしょempNULL!どんなに些細なことでも、あなたの考えが「プラットフォームに大量のメモリが搭載されている場合でも、4GBのRAMは問題ありません。わざわざNULLをチェックする必要はありません」と常にチェックしてください。

バスエラーとは何かを説明するために、他の場所に投稿されたこの質問を見てください。

編集:リンクリスト構造を使用して、関数のパラメーターがどのように呼び出されるかは、関数を理解するために重要です。の使用法に注意してください。これはを指す変数のアドレスを取得し、変数のコピーであるではなく、参照によって渡すことを意味しますこれと同じルールが、一般的なポインタの使用にも適用されます:)

質問の最初のコードでパラメーターが少しずれています。パラメーターリストでダブルポインターを使用している場合は、はい、使用&secondListすると機能します。

于 2012-06-26T16:26:31.353 に答える