0

単純なリンク リストを実装しましたが、使用するすべてのポインターを削除した後、メモリを解放していないようです。ここで欠けているものを誰かが指摘してくれることを望んでいました。アクティビティ モニターを使用して xcode のメモリ使用量を表示していますが、これはメモリ使用量を表示する不正確な方法でしょうか? わからない。コードは次のとおりです。

#include <iostream>
struct node {
    int data;
    node *next;
};

//function to traverse linked list
void traverse(node *root){
    node *trav;

    std::cout << "The list is as follows:\n";
    //traverses through lists
    trav = root;
    do{
        std::cout << (*trav).data << "\n";
        trav = trav->next;
    }while(trav != NULL);
    std::cout << "\n";

    delete trav;
}

void addToList(node *root){
    node *tmp;
    tmp = root;
    if(tmp->next != NULL){
        while(tmp->next !=0){
            tmp = tmp->next;
        }
    }

    //adds new nodes to list
    for(int i = 0 ; i<1000000 ; i++){
        tmp->next = new node;
        tmp = tmp->next;
        tmp->data = i;  
    }
    tmp->next = NULL;

    delete tmp;
}

int main(int argc, const char * argv[])
{   
    node *root;         //this is the root node. it will not change

    //sets up root node
    root = new node;    //root points to a node structure (this is a memory address)
    root->next=NULL;       //next pointer is now "0"
    root->data = 5;     //data in this node = 5

    //print initial list
    traverse(root);
    //add to list
    addToList(root);
    //print new list
    traverse(root);

    delete root;
    return 0;
}

===================編集 - 9:28 AM EST、1 月 27 日===================== ===========

ここに素晴らしいフィードバックをお寄せいただきありがとうございます。私はかなりの数の間違ったことをしてきたことを理解しています (これは私の c++ と動的メモリ割り当ての紹介です。助けに感謝します!)

以下の更新されたコードを見つけてください。私は次の問題に遭遇しました: 1 -- 繰り返しますが、アクティビティ モニターによって報告される実際のメモリ使用量が、これを実行しているプロセスから解放されていません (ターミナルと xcode の両方から実行すると、これが表示されます)。これは、以下の障害が発生した場合に発生しますが、 addToList の for ループを 65514 よりもかなり下にループするように設定した場合にも発生します。--> 修正済み - これはもはや問題ではありません (1 対の括弧が欠落していたため、コードが適切に削除されませんでした。おっと!)

2 -- data = 65514 のノードで、~node の最初の行に EXC_BAD_ACCESS (code 2) が表示されます -- なぜですか? Xcodeは以下を提供します:

Exception State Registers       
trapno  unsigned int    
err unsigned int    
faultvaddr  unsigned long   
Floating Point Registers        
fctrl   unsigned short  
fstat   unsigned short  
ftag    unsigned char   
fop unsigned short  
fioff   unsigned int    
fiseg   unsigned short  
fooff   unsigned int    
foseg   unsigned short  
mxcsr   unsigned int    
mxcsrmask   unsigned int    
stmm0   <invalid>   
stmm1   <invalid>   
stmm2   <invalid>   
stmm3   <invalid>   
stmm4   <invalid>   
stmm5   <invalid>   
stmm6   <invalid>   
stmm7   <invalid>   
xmm0    <invalid>   
xmm1    <invalid>   
xmm2    <invalid>   
xmm3    <invalid>   
xmm4    <invalid>   
xmm5    <invalid>   
xmm6    <invalid>   
xmm7    <invalid>   
xmm8    <invalid>   
xmm9    <invalid>   
xmm10   <invalid>   
xmm11   <invalid>   
xmm12   <invalid>   
xmm13   <invalid>   
xmm14   <invalid>   
xmm15   <invalid>   
General Purpose Registers       
rax unsigned long   
rbx unsigned long   0x0000000000000000
rcx unsigned long   
rdx unsigned long   
rdi unsigned long   
rsi unsigned long   
rbp unsigned long   0x00007fff6ca0c210
rsp unsigned long   0x00007fff6ca0c1c0
r8  unsigned long   
r9  unsigned long   
r10 unsigned long   
r11 unsigned long   
r12 unsigned long   0x0000000000000000
r13 unsigned long   0x0000000000000000
r14 unsigned long   0x0000000000000000
r15 unsigned long   0x0000000000000000
rip unsigned long   0x000000010d60cb96
rflags  unsigned long   
cs  unsigned long   
fs  unsigned long   
gs  unsigned long   

一部のレジスタで見られるように、これは unsigned short の制限と関係があると思いますが、unsigned short は使用していません。??!?!

これが私の現在のコードです:

#include <iostream>
struct node {
    long data;
    node *next;
    node():next(NULL){} //just to be safe, initialize as null to start
    //destructor used to clean up list nodes
    ~node(){
        std::cout << data << "  ";
        if(next != NULL){ 
            std::cout << data << ": deleted\n";
            delete next;
            next = NULL;
        }
    } 
};

//function to traverse linked list
void traverse(node *root){
    node *trav;

    std::cout << "The list is as follows:\n";
    //traverses through lists
    trav = root;
    do{
        std::cout << (*trav).data << "\n";
        trav = trav->next;
    }while(trav != NULL);
    std::cout << "\n";
}

void addToList(node *root){
    node *tmp;
    tmp = root;
    if(tmp->next != NULL){
        while(tmp->next !=0){
            tmp = tmp->next;
        }
    }

    //adds new nodes to list
    for(long i = 0 ; i<100000 ; i++){
        tmp->next = new node;
        tmp = tmp->next;
        tmp->data = i;  
    }
    tmp->next = NULL;    
}


int main(int argc, const char * argv[])
{   
    node *root;         //this is the root node. it will not change

    //sets up root node
    root = new node;    //root points to a node structure (this is a memory address)
    root->next=NULL;       //next pointer is now "0"
    root->data = 5;     //data in this node = 5

    //print initial list
    //traverse(root);
    //add to list
    addToList(root);
    //print new list
    //traverse(root);

    delete root;
    return 0;
}
4

4 に答える 4

0

問題は、ルートノードを削除することですが、ルートノードに直接および間接的にリンクされているすべてのノードも自動的に削除されるわけではありません。

これを実現したい場合は、ポインタが指すオブジェクトのデストラクタnodeを作成できます(そうでない場合) 。これにより、デストラクタ呼び出しのチェーンが展開され、削除しようとしているノードにリンクされているすべてのノードが削除されます。deletenextNULL

または、必要に応じて手動でリストを逆方向に移動し、delete各ノードをトラバースすることもできますが、それはお勧めしません-不必要に複雑でエラーが発生しやすいです。

もう1つの(無関係ですが、悪くはないにしても同様に悪い)問題はにありaddToList()ます。これを行うべきではありません:

delete tmp;

リスト内の前のノードがまだ指しているオブジェクトを削除します。これにより、ダングリングポインターが作成されます(つまり、既存のオブジェクトを指しているように見えますが、実際にはそうではない非NULLポインター)。

traverse()@ us2012が正しく指摘しているように、同様の考慮事項が関数にも当てはまります。

delete trav;

このdelete命令はダングリングポインタを作成しませんが、入力ポインタが存在する場合(これはあなたの場合) 、deleteオペレータは何もしないため、役に立ちません。NULL

于 2013-01-26T17:19:31.073 に答える
0

次の変更を行います

struct node {
    int data;
    node *next;
    node():next(NULL){} //just to be safe
    ~node(){if(next != NULL) delete next;} 
}; 

問題は、ルートのみを削除し、他の要素は削除しないことです。この変更により、ノードを削除するときに、次のノードが存在する場合はそれも削除されるようになります。

最も一般的なソリューションではありませんが、独自の方法でエレガントです。(リストが長すぎる場合はスタックの問題が発生する可能性がありますが、ppetrovによって示されるwhileループにはこの問題はありませんか、それとも末尾再帰ですか?)

delete他の人が言及した他の余分なものも修正する必要があります。

この実装は、リソース取得は初期化と呼ばれる概念に近いものです。これは、スマートポインター、およびライブラリの他の部分が機能するのに役立つ概念です。

于 2013-01-26T17:19:41.263 に答える
0

一般的なガイドは、コードに が含まれている場合、deleteほぼ間違いなく非常に間違っているということです。

struct node {
    int data;
    std::unique_ptr<node> next;
};

これで、それぞれnodeが破棄されるとチェーン内の次nodeのものが破棄されます。つまり、リスト自体がクリーンアップされます。スタックに最初のものを割り当ててnode、スコープ外になったときに解放できるようにするか、別のunique_ptr.

また、リストをトラバースするときにリストを削除したくないことは間違いありません。

于 2013-01-26T17:35:35.147 に答える
0

作成したすべてのノードを削除するわけではありません。ルート ノードから始めて、ノードのすべてのリストを調べて、次々に削除する必要があります (そのために削除機能を作成することを検討する必要があります)。

そんな感じ:

node *actual, *next;
actual = root;

while (actual != null)
{
   next = actual->next;
   delete actual;
   actual = next;
}
于 2013-01-26T17:18:14.717 に答える