2013-03-05 18 views
5

ho pensato che in gcc, void * e char * sono trattati allo stesso modo quando si tratta di puntatori, ossia void * "punti" ad un unico byte di memoria, quindi il seguente codiceC puntatore nullo aritmetica

void *p; 
p = malloc(sizeof(void)); 
printf("%p %p\n",p,p+1); 

restituisce effettivamente 0x984a008 0x984a009. Analogamente, nulli punti ** ad un puntatore, quindi un incremento in un modo veramente un incremento di 4 byte (su un sistema operativo a 32 bit), cioè

void **p; 
p = (void **) malloc(sizeof(void *)); 
printf("%p %p\n",p,p+1); 

rendimenti 0x984a008 0x984a00c. Tuttavia, il seguente codice mi confonde

void **p, *p1; 
p = (void **) malloc(sizeof(void *)); 
p1 = (void **) p; 
printf("%p %p\n",p1,p1+1); 

Dal momento che ritorna di nuovo 0x984a008 0x984a009. Che cosa sta succedendo qui?

+2

Forse volevi dire a digitare 'vuoto ** p, p1 **;' invece di 'void ** p, * p1;'? –

+1

'sizeof (void)' è 1 o almeno dà un avvertimento, è inutile. Non lo fai. Cosa indica un tipo 'vuoto? c'è solo 'void *'. –

+0

volevo dire vuoto ** p, * p1. So che questo codice viene compilato, cosa non capisco, perché se p è stato originariamente dichiarato come void **, p + 1 corrisponde a un incremento di 4 byte, mentre se p1 è dichiarato void * e successivamente rieletto come void **, l'aritmetica aggiunge solo 1 byte per p1 + 1 – Ivan

risposta

1

Il puntatore del vuoto non può essere incrementato. È un comportamento indefinito.

questione connessa: Increment void pointer by one byte? by two?

+0

OK, ma se si cambiano tutti i tipi in puntatori 'char' la domanda rimane valida (sebbene GCC ti fornisca un avvertimento in In quel caso) – Rup

2

Quando si opera con void *, l'incremento è 1. Quando si utilizza void **, è la dimensione di un puntatore.

Nell'operazione che ti confonde, il tuo cast void * in void ** viene implicitamente riportato in un void *. E 'come se avete fatto queste cose:

long a, b, c; 
c = a + (int) b; 

Lanci b a int, ma poi si vuole operare con una long, quindi è gettato indietro.

+0

Va bene, ma perché il compilatore esegue nuovamente la rifusione? Sia "void *" che "void **" prendono 4 byte nello stack e, per quanto ho capito, tutta l'aritmetica del puntatore viene eseguita in fase di compilazione. Quindi, perché il compilatore insiste sul fatto che p1 viene respinto? – Ivan

+0

Pensalo in questo modo: ha senso gettare "void **" in "void *" - qualsiasi puntatore può diventare "void *". Colpendo il contrario, d'altra parte, non la dose. Non tutti i puntatori sono puntatori doppi. Il compilatore fa la cosa sensibile con l'operazione che hai richiesto: ne ha un senso l'unico modo possibile. – slezica

7

Ignorando il possibile comportamento indefinito di void puntatori per il momento ...

Il tipo di p1 è void *.

Non è possibile modificare il tipo di una variabile assegnando ad esso un valore di tipo diverso. p1 rimarrà sempre void *.

Qualsiasi espressione di un tipo diverso assegnato ad esso verrà implicitamente trasmessa a void * (o dare un errore se non può).

Quindi è essenzialmente uguale al primo esempio.

EDIT:

Per quanto ne so, gettando da un tipo ad un altro puntatore in realtà non fare nulla, il suo scopo principale è per il tipo di controllo.

Un puntatore è solo un indirizzo di memoria, un numero, così essenzialmente la memoria sembra qualcosa di simile: (post-assegnazione)

p1  p2 
void * void** <- these types are fixed and known during compilation 
------ ------ 
|1234| |1234|   at address 1234 = the 4 bytes from malloc 
------ ------ 
^
    | 
this value is the only thing that will change by assigning p1 to a different value 
+3

Su alcune architetture, la trasmissione da un tipo di puntatore all'altro cambia qualcosa. Sulla tua tipica architettura x86 (_64), però, puoi aspettarti che tutti i puntatori abbiano la stessa rappresentazione e che i cast siano un no-op. –

3

Si consiglia di utilizzare char * invece di void *, dal momento che l'aritmetica su puntatore per void è un'estensione gcc.

char *p1 = /* ... */; 

printf("%p %p\n", p1, p1+1); 

Qualunque siano punti p, l'aritmetica puntatore sullo p usa char * tipo (non char **).

se si scrive:

char *p1 = /* ... */; 

printf("%p %p\n", p1, (char**)p1+1); 

aritmetica dei puntatori utilizza char **.

0

So che sto postando più di un anno dopo, ma mi è successo su questa domanda e mi ha incuriosito.

Sono d'accordo con @Dukeling che non è possibile modificare il tipo di una variabile solo lanciandolo. Ma sembra che dipenda da ciò che il compilatore ritiene essere void. Prendi questo programma di esempio e guarda l'output risultante. Si noti che l'unica differenza tra vp e vp2 è la porzione sizeof() di malloc().

compilato su: gcc (Debian 4.7.2-5) 4.7.2
Compile linea: gcc -o void_test void_test.c

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

int main(int argc, char **argv) { 
    void *vp, *vp2; 

    printf("sizeof(void) = %d\n", sizeof(void)); 
    printf("sizeof(void *) = %d\n", sizeof(void *)); 
    printf("sizeof(char) = %d\n", sizeof(char)); 
    printf("sizeof(char *) = %d\n\n", sizeof(char *)); 

    vp = (void *) malloc(sizeof(void)); 
    vp2 = (void *) malloc(sizeof(void *)); 

    printf("vp = %p\n", vp); 
    printf("vp+1 = %p\n", vp+1); 
    printf("vp2 = %p\n", vp); 
    printf("vp2+1 = %p\n", vp2+1); 

    return 0; 
} 

ha pronunciato la seguente uscita:

$ ./void_test 
sizeof(void) = 1 
sizeof(void *) = 8 
sizeof(char) = 1 
sizeof(char *) = 8 

vp = 0x1ee3010 
vp+1 = 0x1ee3011 
vp2 = 0x1ee3010 
vp2+1 = 0x1ee3031