Perché un int lungo richiede 12 byte su alcune macchine?


26

Ho notato qualcosa di strano dopo aver compilato questo codice sulla mia macchina:

#include <stdio.h>

int main()
{
    printf("Hello, World!\n");

    int a,b,c,d;

    int e,f,g;

    long int h;

    printf("The addresses are:\n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x",
        &a,&b,&c,&d,&e,&f,&g,&h);

    return 0;
}

Il risultato è il seguente. Si noti che tra ogni indirizzo int esiste una differenza di 4 byte. Tuttavia tra l'ultimo int e il lungo int c'è una differenza di 12 byte:

 Hello, World!
 The addresses are:

 da54dcac 
 da54dca8 
 da54dca4 
 da54dca0 
 da54dc9c 
 da54dc98 
 da54dc94 
 da54dc88

3
Metti un altro intdopo hnel codice sorgente. Il compilatore potrebbe metterlo nel gap, prima h.
ctrl-alt-delor,

32
Non utilizzare la differenza tra gli indirizzi di memoria per determinare la dimensione. C'è una sizeoffunzione per questo. printf("size: %d ", sizeof(long));
Chris Schneider,

10
Stai solo stampando i 4 byte bassi dei tuoi indirizzi con %x. Fortunatamente per te, sembra funzionare correttamente sulla tua piattaforma per passare argomenti di puntatore con una stringa di formato in attesa unsigned int, ma puntatori e ints hanno dimensioni diverse in molte ABI. Utilizzare %pper stampare puntatori in codice portatile. (È facile immaginare un sistema in cui il tuo codice stamperebbe la metà superiore / inferiore dei primi 4 puntatori, invece della metà inferiore di tutti gli 8.)
Peter Cordes,


2
@luu non diffonde disinformazione. Nessun compilatore decente si preoccupa dell'ordine in cui vengono dichiarate le variabili in C. Se ci tiene, non c'è motivo per cui lo farebbe nel modo in cui lo descrivi.
gnasher729,

Risposte:


81

Non ci sono voluti 12 byte, ci sono voluti solo 8. Tuttavia, l' allineamento predefinito per un int lungo 8 byte su questa piattaforma è 8 byte. Pertanto, il compilatore doveva spostare il long int su un indirizzo divisibile per 8. L'indirizzo "ovvio", da54dc8c, non è divisibile per 8, quindi il gap di 12 byte.

Dovresti essere in grado di testarlo. Se aggiungi un altro int prima del long, quindi ce ne sono 8, dovresti scoprire che il long int sarà allineato bene senza mossa. Ora saranno solo 8 byte dall'indirizzo precedente.

Probabilmente vale la pena sottolineare che, sebbene questo test dovrebbe funzionare, non dovresti fare affidamento sulle variabili organizzate in questo modo. Il compilatore AC è autorizzato a fare ogni sorta di cose funky per provare a far funzionare rapidamente il programma, comprese le variabili di riordino (con alcune avvertenze).


3
differenza, non gap.
Deduplicatore,

10
"comprese le variabili di riordino". Se il compilatore decide di non utilizzare due variabili contemporaneamente, è libero di sovrapporle parzialmente o anche di sovrapporle completamente ...
Roger Lipscombe,

8
O in effetti, tenerli nei registri anziché nello stack.
Smetti di fare del male a Monica il

11
@OrangeDog Non penso che succederebbe se l'indirizzo fosse preso come in questo caso ma, in generale, hai ovviamente ragione.
Alex,

5
@Alex: puoi ottenere cose divertenti con memoria e registri quando prendi l'indirizzo. Prendere l'indirizzo significa che deve dargli una posizione di memoria, ma non significa che debba effettivamente usarlo. Se prendi l'indirizzo, gli assegni 3 e lo passi a un'altra funzione, potrebbe semplicemente scrivere 3 in RDI e chiamare, non scriverlo mai in memoria. A volte sorprendente in un debugger.
Zan Lynx,

9

Questo perché il compilatore sta generando ulteriore riempimento tra le variabili per assicurarsi che siano allineate correttamente in memoria.

Sulla maggior parte dei processori moderni, se un valore ha un indirizzo che è un multiplo della sua dimensione, è più efficiente accedervi. Se fosse stato messo hnel primo punto disponibile, il suo indirizzo sarebbe stato 0xda54dc8c, che non è un multiplo di 8, quindi sarebbe stato meno efficiente da usare. Il compilatore lo sa e sta aggiungendo un po 'di spazio inutilizzato tra le ultime due variabili per assicurarsi che accada.


Grazie per la spiegazione. Potete indicarmi alcuni materiali riguardo ai motivi per cui l'accesso a variabili che sono multiple delle loro dimensioni è più efficiente? vorrei sapere perché sta succedendo questo?
yoyo_fun,

4
@yoyo_fun e se vuoi davvero capire la memoria, allora c'è un famoso articolo sull'argomento futuretech.blinkenlights.nl/misc/cpumemory.pdf
Alex

1
@yoyo_fun È abbastanza semplice. Alcuni controller di memoria possono accedere solo a multipli della larghezza di bit del processore (ad es. Un processore a 32 bit può solo richiedere direttamente gli indirizzi 0-3, 4-7, 8-11, ecc.). Se si richiede un indirizzo non allineato, il processore deve effettuare due richieste di memoria, quindi inserire i dati nel registro. Quindi, tornando a 32 bit, se si desidera un valore memorizzato all'indirizzo 1, il processore deve richiedere gli indirizzi 0-3, 4-7, quindi ottenere i byte da 1, 2, 3 e 4. Quattro byte di memoria letta sprecata.
phyrfox,

2
Punto minore, ma l'accesso alla memoria disallineato può essere un errore irrecuperabile invece di un impatto sulle prestazioni. Dipende dall'architettura.
Jon Chesterfield,

1
@JonChesterfield - Sì. Ecco perché ho commentato che la descrizione che ho dato si applica alla maggior parte delle architetture moderne (con cui intendevo principalmente x86 e ARM). Ce ne sono altri che si comportano in modi diversi, ma sono sostanzialmente meno comuni. (È interessante notare che ARM era una delle architetture che richiedevano un accesso allineato, ma hanno aggiunto la gestione automatica dell'accesso non allineato nelle revisioni successive)
Jules,

2

Il tuo test non sta necessariamente testando ciò che pensi che sia, perché non è necessario che la lingua metta in relazione l'indirizzo di nessuna di queste variabili locali.

Dovresti metterli come campi in una struttura per poter dedurre qualcosa sull'allocazione della memoria.

Le variabili locali non sono tenute a condividere l'archiviazione una accanto all'altra in alcun modo particolare. Il compilatore può inserire una variabile temporanea in qualsiasi punto dello stack, ad esempio, che potrebbe trovarsi tra due di queste variabili locali.

Al contrario, non sarebbe consentito inserire una variabile temporanea in una struttura, quindi se invece si stampassero gli indirizzi dei campi della struttura, si confronterebbero gli oggetti destinati allocati dallo stesso blocco logico di memoria (la struttura).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.