2016-10-07 1 views
1

Ich habe eine dornige C-Syntax Frage. Ich baue ein Array von verknüpften Listen auf, wobei jeder Knoten in einer Liste durch eine Struktur repräsentiert wird. Jede Struktur hat einen String, der später wichtig ist:Deref einen C-Zeiger auf eine Zeichenfolge innerhalb einer Struktur innerhalb eines Arrays?

// "linkedList.h" 

typedef struct llnode listnode; 
struct llnode { 
    char* data;  // string 
    // ...and other data 
}; 

Mein Code erstellt eine Tabelle von Zeigern auf dieses „listnodes“ und setzt all diese Zeiger auf NULL, aus Gründen, über den Rahmen dieses Beitrags:

void initTable(listnode** table){ 
    // Initialize all pointers in the table to NULL 
    for (int i = 0; i < TABLESIZE; i++) 
     table[i] = NULL; 
} 

int main(){  
    // Create table of linked lists 
    listnode** table = (listnode**) malloc(TABLESIZE * sizeof(listnode*)); 
    initTable(table); 

    return 1; 
} 

So weit, so gut. Später stopft mein Programm Daten in die Tabelle und fügt sie bei Bedarf der rechten Liste hinzu. Der Code zu tun, das funktioniert, aber ich werde eine stark vereinfachte Version hier aus Gründen halten meinen Beitrag so kurz wie möglich darbringen:

void insert(listnode** table, int index, char* newData){ 
    if(*(table+index)==NULL){ 
     // Create the new Node 
     listnode *newNode = NULL; 
     newNode = (listnode*)malloc(sizeof(listnode)); // allocate for the struct 
     newNode->data = (char*)malloc(sizeof(char)*15); // allocate for the string within the struct 
     strcpy(newNode->data, newData);     // copy newData into newNode->data 

     // Insert node into table (Super simple version) 
     *(table+index) = newNode; 
    } 
} 

int main(){ 
    listnode** table = (listnode**) malloc(TABLESIZE * sizeof(listnode*)); 
    initTable(table); 

    insert(table, 0, "New String A"); 
    insert(table, 5, "New String B"); 
    insert(table, 7, "New String C"); 

    return 1; 
} 

Das alles funktioniert super. Nun zu meiner eigentlichen Frage ... Angenommen, ich möchte in den Tisch greifen und eine dieser Strings dereferenzieren?

void printTable(listnode** table){ 
    for(int i=0; i<TABLESIZE; i++){ 
     if(*(table+i)==NULL) 
      printf("table[%d] == NULL\n", i); 
     else 
      printf("table[%d] == %s\n", i, **(table+i)->data); // << PROBLEM IS HERE! 
    } 
} 

int main(){ 
    // create & initialize the table, as above 
    // insert data into the table, as above 
    printTable(table); 

    return 1; 
} 

Der Compiler nicht mag meine Syntax:

$ gcc -Wall linkedList.c 
linkedListc: In function ‘printTable’: 
linkedList.c:31:48: error: request for member ‘data’ in something not a structure or union 
    printf("table[%d] == %s\n", i, **(table+i)->data); 
               ^
$ 

So weiß ich, das ist irgendwie eine lange Aufzug Voraussetzung für eine einfache Frage, aber kann mir jemand helfen mit der richtigen Syntax hier? Ich habe eine Reihe von syntaktischen Variationen ausprobiert, ohne Glück.

Mehr rätselhaft, wenn ich den Code ein wenig zu kompilieren, dann schauen Sie sich dies in GDB, ich kann sehen, dass **(table+i) ist meine Struktur, noch **(table+i)->data ist nicht zugänglich. Hier ist die GDB-Ausgabe, wenn ich das (modifizierte) Programm debugge; Der Knoten, der "Neue Zeichenfolge A" darstellt, wird zuerst an Index 0 in der Tabelle eingefügt. :

31   printf("table[%d] == %d\n", i, **(table+i)); 
(gdb) p *(table+i) 
$1 = (listnode *) 0x6000397b0 
(gdb) p **(table+i) 
$2 = {data = 0x6000397d0 "New String A"} 
(gdb) p **(table+i)->data 
Cannot access memory at address 0x4e 
(gdb) 

Ich bin wirklich darüber verwirrt. Sobald ein C-Zeiger mehr als eine Dereferenzierungsschicht durchläuft, werde ich schielen. Wer weiß, wie die richtige Syntax hier aussehen könnte?

Dank einer Million, -Pete

PS - Apologies für den superlangen Beitrag. Ich schwöre, ich hatte Mühe, es überschaubar zu halten ...

+0

Erstens, anstatt sie null zu setzen, warum nicht Calloc? Zweitens ist es nicht Ihre Syntax, dass das Objekt falsch ausgerichtet ist - versuchen Sie einen Cast. – cat

+0

Ich denke nicht, dass Ihre Beispiele ausreichen, um dies zu reproduzieren, um eine Antwort zu schreiben, in Betracht ziehen, eine [mcve] – cat

Antwort

3

der Erklärung

listnode **table; 
Da

dann haben die folgenden Begriffe die angegebenen Typs

Expression    Type 
    ----------    ---- 
     table    listnode ** 
    table + i    listnode ** 
     *table    listnode * 
*(table + i)    listnode * 
    table[i]    listnode * 
**(table + i)    listnode 
    *table[i]    listnode 

So würden Sie eine der folgenden Ausdrücke verwenden, um die data Mitglied zuzugreifen, aus den am wenigsten zu den meisten Augen Stabby:

table[i]->data  // use this, please 
(*table[i]).data 
(*(table + i))->data 
(**(table + i)).data 

Die Gruppierung Parens sind notwendig - die . und -> Mitgliederauswahloperatoren haben eine höhere Priorität als unäre *, also würde *table[i].data als *(table[i].data) geparst werden, was nicht das ist, was Sie wollen.

+0

Danke, das ist super hilfreich! Ich füge all das in meine C-Notizen ein. Ich muss sagen, C-Syntax ist wirklich, wirklich schwierig - wie lange dauert es, all das Zeug zu lernen? Jahre? Wütend...! – Pete

+1

@Pete: C Ausdruck Syntax ist nicht * schwierig *, nur beteiligt (und in einigen Fällen nicht intuitiv). Es sieht nur schwierig aus, weil sich niemand die Zeit nimmt, es richtig zu erklären. Wenn ich eine C-Klasse unterrichte (was niemals passieren wird, danke * deinen * Glückssternchen), würde ich mindestens die Hälfte des Semesters über Ausdrücke und Typen gehen. –

+0

Nun, Gott segne Sie, dass Sie sich die Zeit genommen haben, es mir zu erklären. Ich finde, dass 90% meiner C-Fragen auf die Spezifika der C-Syntax hinauslaufen - es ist ein Trost zu wissen, dass es vielleicht nicht so komplex und dornig ist, wie ich anfangs dachte. Danke noch einmal! – Pete

2

Der Operator C a->b wertet (*a).b aus. Ihre Zeile ergibt also tatsächlich etwas wie ***(table+i).data, was offensichtlich falsch ist.

Es kann auch helfen, mit Klammern zu gruppieren, so dass es klar ist, ob die Zeile (***(table+i)).data oder ***((table+i).data) auswertet. Basierend auf der Fehlermeldung, die Sie erhalten, ist es wahrscheinlich Letzteres.

Und dann können Sie die Array-Syntax verwenden, um es mehr zu bereinigen.

Alle zusammen können Sie die Linie zu vereinfachen.

Warum hat Ihre Debugging-Sitzung sonst etwas angezeigt? Weil ** (table + i) das eigentliche struct selbst ist. Um -> zu verwenden, benötigen Sie einen Zeiger auf die Struktur.

Auch ein kleiner Tipp. Sie haben Ihre malloc() -Aufrufe getätigt.Im Allgemeinen vermeiden Programmierer dies aus verschiedenen Gründen. Es gibt mehrere Beiträge dazu, aber eine der besten kann gefunden werden here.

+0

Vielen Dank, das hilft sehr. Ich dachte, ich habe versucht, Tabelle [i] -> Daten, aber offensichtlich habe ich nicht. Es ist jetzt in meinem Code und die ganze Sache funktioniert. Sehr geschätzt! – Pete

1

Ok, so -> ist etwas, das genannt war ursprünglich ein primären Betreiber und wird nun in der C11 als postfix Ausdruck angegeben. Es bindet enger als ein unärer Operator. Die -> ist wie . in dem es die höchste Priorität von allen hat.

Fall 1: einen indirekten Zeiger zu verwenden, könnte man folgendes tun:

(*p)->field // or... 
p[0]->field 

Fall 2: heraus auf dem lunatic fringe der indirekten Zeiger, können Sie natürlich auch weiterhin diese mit ..

.
(**p)->field // or ... 
p[0][0]->field 

Aber sobald man die Pars in dort werfen, könnte man einfach in den resultierenden Ausdruck Punkt wie unten. Using -> setzt einige der Deref in der ->-Operator und einige davon in der *; Es könnte ein Argument für die Verwendung des einen oder anderen geben.

(**p).field  // case 1 
p[0][0].field // still case 1 
(***p).field  // case 2 
p[0][0][0].field // still 2 
+0

Danke, das ist klar und zum Nachdenken anregen. Ich habe einen langen Weg vor mir, bevor ich die C-Syntax wirklich verstehe. Vielen Dank! – Pete

Verwandte Themen