2016-06-29 42 views
5

Considerare:Perché * curr e curr-> val lo stesso valore?

#include<stdlib.h> 
#include<stdio.h> 

struct node { 
    int val; 
    struct node *next; 
}; 

typedef struct node item; 

void main() { 
    item * curr, *head; 
    int i; 

    head = NULL; 
    for (i = 1; i <= 10; i++) { 
     curr = (item *)malloc(sizeof(item)); 

     curr->val = i; 
     curr->next = head; 
     head = curr; 
    } 

    curr = head; 
    while (curr) { 
     printf("%d\n", curr->val); 
     printf("%d\n", *curr); 
     curr = curr->next; 
    } 

    getchar(); 
} 

Come ho stampare, *curr e curr->val sono gli stessi. Perché?

Sono confuso circa *curr. Penso che *curr sia il valore &curr punti a. (Sono novizio in C.)

+0

Se si compila con avvisi, la linea "printf (" % d \ n "* curr);" dovrebbe stampare un avviso poiché struct node non è un numero intero. Mentre c'è un motivo per cui si vede questo comportamento, è un po 'complicato e ha a che fare con il modo in cui i vararg vengono gestiti in C. Probabilmente dovresti semplicemente evitare di farlo in modo che il comportamento sia più comprensibile. –

+3

'printf ("% d \ n ", * curr);' è un comportamento indefinito perché '* curr' non è un int. – immibis

+1

In aggiunta a ciò che i commenti precedenti hanno correttamente indicato, per riferimento, l'accesso agli elementi con '(* curr) .val' è uguale a' curr-> val' – wolfsgang

risposta

-4

Qui

curr->val = i; 
curr->next = head; 
head = curr; 

prima si imposta (*curr).val a i. Quindi fai il punto curr->next a head. In questo momento ancora NULL. Quindi fai il punto head a curr. E qui la parte interessante, curr punta al suo inizio e *curr dereferenze che è essenzialmente curr->val, perché da lì inizia la struct curr.

Secondo standard e questo answer

(C1X §6.7.2.1.13: "Un puntatore a un oggetto struttura, opportunamente convertito, indica suo membro iniziale ... e viceversa ci possono. tramite imbottitura anonimo all'interno come oggetto la struttura, ma non al suo inizio. ")

che significa che non v'è alcun spazio (padding) (o anche ad esempio alcune HEAD informazioni) tra l'inizio attuale struttura e l'inizio del suo primo elemento.

MODIFICA:
Non dico nulla di UB, non perché non lo vedo. OP ha chiesto qualcos'altro. Dovrebbe essere indicato, ma non essere la risposta! Ecco perché abbiamo la sezione commenti!

+1

'curr' punta a' & (curr-> val) 'ma' * curr' è ** non ** 'curr-> val' inizia solo con' curr-> val' ma è più lungo. Vedi la mia risposta per maggiori dettagli –

0

Questo comportamento non è definito e fornirà avvisi.

printf ottiene il tipo di argomento che viene passato ad esso con l'aiuto degli specificatori di formato. Nel tuo caso il primo elemento della struttura è un int e quindi ha uno int in cima allo stack. Quindi quando printf cerca uno int ne trova uno e fornisce l'output corretto, anche se hai passato l'intera struttura.

Se esisteva un altro tipo di dati diverso da int come primo elemento della struttura, avrebbe dato un risultato indefinito. Quindi, come ho menzionato nei commenti, i modi corretti per accedere agli elementi della struttura sono con (*curr).value o come si è utilizzato curr->value.

+1

Si dice che il codice è un comportamento indefinito, ma in seguito si dice che "se c'era un altro tipo di dati diverso da' int' ... avrebbe dato un risultato indefinito ". Sicuramente se è un comportamento indefinito l'output è già indefinito, non importa il tipo? –

+0

@PaulHankin Vero, l'ho scritto giusto per chiarire l'output corretto in questo caso e come non lo sarà per altri casi. – wolfsgang

9

Prima di cominciare, dovresti provare a scrivere un codice corretto che non sollevi avvisi prima di cercare di capire cosa succede sotto il cofano e perché alcune espressioni errate danno ancora risultati corretti.

C è un linguaggio molto permissivo, cioè finché il compilatore è in grado di tradurre ciò che legge presuppone generalmente che il programmatore sappia perché l'ha scritto. È quello che succede qui.

In C, l'indirizzo di un struct può essere inoltrato a un indirizzo al suo primo elemento. Quindi (void *) &curr corrisponde a (void *) &(curr->val). Qui lancio tutto a void * per assicurarmi di avere tipi di puntatori compatibili.

Ciò significa che *((int *) curr) è un'espressione C perfettamente definita e il suo valore è effettivamente curr->val. Ma è corretto, perché ho scritto un cast puntatore esplicito.

Cosa hai scritto funziona solo per errore, perché come curr è un tipo aggregato; si passa un int ed un item *-printf e utilizzare solo il suo primo valore - questo è non è più di serie C e posso solo supporre un classico implementazione intuire che. Ma basta sostituire i comandi di stampa dal che:

printf("%d - %d\n", curr->val, 12); 
printf("%d - %d\n", *curr, 15); 

Si dovrebbe vedere spazzatura nel secondo printf, perché con %d si dichiara che si passa un semplice int ed effettivamente passare un aggregato. Anche in questo caso, nulla è esplicitamente richiesto per standard, ma un'implementazione comune dovrebbe dare questo risultato.

TL; DR: Come *curr non è un int, printf("%d\n", *curr); è un comportamento indefinito. Ciò significa che tutto può accadere, anche il risultato atteso, ma una versione diversa dello stesso compilatore, o un altro compilatore, o anche un cambiamento nei parametri di compilazione potrebbe dare un risultato diverso, così come un piccolo cambiamento nel codice.

E C consente il cast automatico di puntatore ae da void * e non si dovrebbe mai trasmettere il valore restituito di malloc in quanto può nascondere i bug. Si prega di scrivere:

curr = malloc(sizeof(item)); 

Non è più C++ compatibili, ma è corretto C.

Problemi correlati