Domanda

In C #, è possibile dichiarare una struct (o classe) che dispone di un membro di tipo di puntatore, in questo modo:

unsafe struct Node
{
  public Node* NextNode;
}

E 'mai al sicuro (err .. ignorare per un momento che ironico bandierina unsafe ..) per utilizzare questa costruzione? Voglio dire per la conservazione a lungo termine sul mucchio. Da quello che ho capito, il GC è libero di muovere le cose intorno, e mentre aggiorna i riferimenti a qualcosa che è stato spostato, ci si aggiorna puntatori troppo? Sto indovinando no, che renderebbe questa costruzione molto pericoloso, giusto?

Sono sicuro che ci sono alternative modo superiore a fare questo, ma lo chiamo curiosità morbosa.

EDIT: Sembra che ci sia una certa confusione. So che questo non è un grande cantiere, I puramente vogliono sapere se questo è sempre una costruzione sicura, vale a dire: è il puntatore garantito di tenere puntato a tutto ciò che in origine la puntò alla

Il C-codice originale è stato usato per attraversare un albero (profondità prima) senza ricorsione, dove l'albero è memorizzato in una matrice. La matrice viene quindi attraversato da incrementando un puntatore, a meno che una certa condizione è soddisfatta, allora il puntatore viene spostato il NextNode, dove attraversamento continua. Naturalmente, lo stesso può in C # essere realizzato:

struct Node
{
  public int NextNode;
  ... // other fields
}

Se il int è l'indice nella matrice del nodo successivo. Ma per motivi di prestazioni, sarei andato a finire a giocherellare con i puntatori e gli array fixed per evitare controlli Bounds in ogni caso, e sembrava più naturale il C-codice originale.

È stato utile?

Soluzione

  

E 'sempre sicuro da usare questa costruzione? Voglio dire per la conservazione a lungo termine sul mucchio.

Sì. In questo modo è di solito insensato, doloroso e inutile, ma è possibile.

  

Da quello che ho capito, il GC è libero di muovere le cose intorno, e mentre aggiorna i riferimenti a qualcosa che è stato spostato, ci si aggiorna puntatori troppo?

No. Per questo facciamo si contrassegna come non sicuro.

  

Sto cercando di indovinare no, che renderebbe questa costruzione molto pericoloso, giusto?

Una corretta.

  

Sono sicuro che ci sono alternative modo superiore a fare questo, ma lo chiamo curiosità morbosa.

Ci sono certamente.

  

è il puntatore garantito di tenere puntato a tutto ciò che in origine la puntò a?

No, a meno ci si assicura che ciò accada. Ci sono due modi per farlo.

Way uno: Dite al garbage collector di non spostare la memoria. Ci sono due modi per farlo:

  • Fissare una variabile in atto con l'affermazione "fisso".

  • Utilizzare i servizi di interoperabilità per creare una maniglia gc alle strutture che si desidera mantenere viva e in un unico luogo.

Fare una di queste cose ad alta relitto probabilità che le prestazioni del garbage collector.

Way due: non prendere i riferimenti alla memoria che il garbage collector può assolutamente muoversi. Ci sono due modi per farlo:

  • prendere solo indirizzi delle variabili locali, parametri di valore, o blocchi stack-allocati. Naturalmente, in questo modo si sono poi tenuti a garantire che i puntatori non sopravvivono più a lungo lo stack frame in questione, in caso contrario, si sta riferimento spazzatura.

  • allocare un blocco fuori dal mucchio non gestito e quindi utilizzare i puntatori all'interno di quel blocco. In essenza, implementare il proprio gestore di memoria. Si sono tenuti a implementare correttamente il nuovo gestore di memoria personalizzata. Fate attenzione.

Altri suggerimenti

Perché non:

struct Node
{
    public Node NextNode;
}

o almeno:

struct Node
{
    public IntPtr NextNode;
}

È possibile utilizzare il dichiarazione fisso per evitare che il GC spostare puntatori intorno.

Sì, il garbage collector può spostare gli oggetti intorno e, no, non aggiornerà i puntatori. È necessario fissare gli oggetti si punta. Maggiori informazioni possono essere trovate su questo gestione della memoria spiegazione .

È possibile risolvere gli oggetti come questo:

  unsafe {
     fixed (byte* pPtr = object) {
         // This will fix object in the memory
        }
     }
  }

I vantaggi di puntatori sono di solito le prestazioni e l'interazione con altro codice non sicuro. Non ci saranno controlli out-of-bounds ecc, accelerando il codice. Ma, proprio come se si stesse programmando in es C bisogna stare molto attenti a quello che si sta facendo.

Alcuni controlli di integrità evidenti sono stati esclusi. Il problema evidente con questo è che bisogna allocare più è necessario perché non si può riallocare il buffer come la parola fisso implica.

public unsafe class NodeList
{
    fixed Node _Nodes[1024];
    Node* _Current;

    public NodeList(params String[] data)
    {
        for (int i = 0; i < data.Length; i++)
        {
            _Nodes[i].Data = data[i];
            _Nodes[i].Next = (i < data.Length ? &_Nodes[i + 1] : null);     
        }

        _Current = &_Nodes[0];
    }

    public Node* Current()
    {
        return _Current++;  
    }
}

public unsafe struct Node
{
    public String Data;
    public Node* Next;
}

Un un'idea pericolosa, ma può funzionare:

Quando l'array di struct supera una certa dimensione (85000 byte) verrà allocato sulla Large Object Heap in cui i blocchi vengono esaminati e raccolti, ma non spostati ...

L'articolo collegato sottolinea il pericolo che una versione più recente CLR potrebbe spostare roba sul LOH ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top