2013-02-21 12 views
5

Una domanda specifica per C++. Quindi ho letto una domanda su cosa rende un programma a 32 bit/64 bit, e l'anwser è stato qualcosa di simile (mi spiace non posso trovare la domanda, un giorno fa l'ho guardato e non posso più ritrovarlo :(): Finché non crei alcuna "ipotesi di puntatore", hai solo bisogno di ricompilarlo Quindi la mia domanda è, che cosa sono le presunte puntatori? A mio avviso c'è un puntatore a 32 bit e puntatori a 64 bit quindi immagino sia qualcosa a che fare con Si prega di mostrare la diffrenza di codice tra di loro.Tutte le altre buone abitudini da tenere a mente mentre si scrive codice, che aiuta a rendere facile la conversione tra i sono anche benvenuti :) Per favore, condividi esempi con loroCome scrivere codice convertibile, 32 bit/64 bit?

Ps . So che c'è questo post: How do you write code that is both 32 bit and 64 bit compatible? ma per quanto mi riguarda era una specie di generalità senza buoni esempi, per i nuovi programmatori come me. Come quello che è un ect di storage a 32 bit. Un po 'saltellando per abbatterlo un po' di più (nessun gioco di parole destinato ^^) ds.

risposta

3

Per un programma ben formato (ovvero un programma scritto in base alla sintassi e regole semantiche di C++ senza comportamento indefinito), lo standard C++ garantisce che il programma disponga di un insieme di comportamenti osservabili. I comportamenti osservabili variano a causa di comportamenti non specificati (incluso il comportamento definito dall'implementazione) all'interno del programma. Se si evita un comportamento non specificato o lo si risolve, al programma verrà garantito un output specifico e sicuro. Se scrivi il tuo programma in questo modo, non vedrai differenze tra il tuo programma su una macchina a 32 o 64 bit.

Un semplice (forzata) esempio di un programma che avrà diverse possibili uscite è la seguente:

int main() 
{ 
    std::cout << sizeof(void*) << std::endl; 
    return 0; 
} 

Questo programma sarà probabilmente avere output diverso su macchine 32 e 64 bit (ma non necessariamente). Il risultato di sizeof(void*) è definito dall'implementazione. Tuttavia, è certamente possibile avere un programma che contiene un comportamento definito dall'implementazione, ma si risolve per essere ben definito:

int main() 
{ 
    int size = sizeof(void*); 
    if (size != 4) { 
    size = 4; 
    } 
    std::cout << size << std::endl; 
    return 0; 
} 

Questo programma sarà sempre stampare 4, nonostante il fatto che utilizza il comportamento definito dall'implementazione. Questo è un esempio stupido perché potremmo aver appena fatto int size = 4;, ma ci sono casi in cui questo appare nella scrittura del codice indipendente dalla piattaforma.

Quindi la regola per scrivere codice portatile è: mirare a evitare o risolvere il comportamento non specificato.

Ecco alcuni consigli per evitare comportamenti non specificata:

  1. non date per scontato nulla circa la dimensione dei tipi fondamentali di là di quello che il C++ specifica standard. Vale a dire, un char è almeno 8 bit, sia short e int sono almeno 16 bit e così via.

  2. Non tentare di eseguire il puntatore magico (fusione tra tipi di puntatore o memorizzazione di puntatori in tipi interi).

  3. Non utilizzare un unsigned char* per leggere la rappresentazione del valore di un oggetto non char (per la serializzazione o attività correlate).

  4. Evitare reinterpret_cast.

  5. Prestare attenzione quando si eseguono operazioni che possono sopra o sotto. Pensa attentamente quando esegui operazioni di spostamento di bit.

  6. Fare attenzione quando si esegue l'aritmetica sui tipi di puntatore.

  7. Non utilizzare void*.

Ci sono molte altre occorrenze di comportamento non specificato o non definito nello standard. Vale la pena cercarli. Ci sono some great articles online che coprono alcune delle differenze più comuni che si verificano tra le piattaforme a 32 e 64 bit.

+0

awsome anwser :) proprio quello che stavo cercando :) –

+0

@FredrikBostonWestman, allora dovresti accettarlo! – gsamaras

3

"Presupposti puntatori" è quando si scrive codice che si basa su indicatori che si adattano ad altri tipi di dati, ad es. int copy_of_pointer = ptr; - se int è un tipo a 32 bit, questo codice verrà interrotto su macchine a 64 bit, poiché verrà memorizzata solo una parte del puntatore.

Fintanto che i puntatori vengono memorizzati solo nei tipi di puntatore, non dovrebbe esserci alcun problema.

In genere, i puntatori sono la dimensione della "parola macchina", quindi su un'architettura a 32 bit, 32 bit e su un'architettura a 64 bit, tutti i puntatori sono a 64 bit. Tuttavia, ci sono alcune architetture in cui questo non è vero. Non ho mai lavorato su queste macchine [oltre a x86 con i suoi puntatori "lontani" e "vicini", ma per ora lo ignoro].

La maggior parte dei compilatori ti dirà quando convertirai i puntatori in numeri interi in cui il puntatore non si adatta, quindi se attivi gli avvisi, la MAGGIOR PARTE dei problemi diventerà evidente - correggi gli avvisi, e le probabilità sono abbastanza buone che il tuo codice funzionerà subito

1

Non vi sarà alcuna differenza tra codice a 32 bit e codice a 64 bit, l'obiettivo di C/C++ e altri linguaggi di programmazione sono la loro portabilità, anziché il linguaggio assembly.

L'unica differenza sarà la distribuzione su cui compilerai il tuo codice, tutto il lavoro viene fatto automaticamente dal tuo compilatore/linker, quindi non pensarci.

Ma: se si sta programmando su una distribuzione a 64 bit ed è necessario utilizzare una libreria esterna ad esempio SDL, la libreria esterna dovrà essere compilata anche a 64 bit se si desidera compilare il codice.

Una cosa da sapere è che il file ELF sarà più grande su una distribuzione a 64 bit che su uno a 32 bit, è solo una logica.

Qual è il punto con il puntatore? quando si aumenta/modifica un puntatore, il compilatore incrementerà il puntatore dalla dimensione del tipo di puntamento.

La dimensione del tipo contenuto è definita dalla dimensione del registro del processore/dalla quale viene distribuito il lavoro.

Ma non devi preoccuparti di questo, la compilation farà tutto per te.

Somma: Ecco perché non è possibile eseguire un file ELF a 64 bit su una distribuzione a 32 bit.

+0

quando dici disrib si Meen? –

+1

@FredrikBostonWestman: questa risposta presuppone un moderno sistema operativo basato su Linux/BSD. Una "implementazione" di tale sistema operativo è chiamata distribuzione; sono progettati come un pacchetto completo stabile in cui tutto è specificamente compilato per l'architettura su cui verrà eseguito. È difficile ottenere tali benefici con la tecnologia closed-source, ovviamente ... – leftaroundabout

6

In generale significa che il comportamento del programma non deve mai dipendere dallo sizeof() di alcun tipo (che non sono fatti per essere di una certa dimensione), né esplicitamente né implicitamente (questo include anche possibili allineamenti di struttura).

puntatori sono solo un sottoinsieme di essi, e probabilmente significa anche che non si dovrebbe cercare di fare affidamento sul fatto in grado di convertire tra i tipi di puntatore non collegati e/o interi, a meno che non sono specificamente realizzati per questo (ad esempio intptr_t).

Allo stesso modo è necessario occuparsi di cose scritte su disco, dove non si dovrebbe mai fare affidamento sulle dimensioni di ad es. costruito in tipi, essendo lo stesso ovunque.

Ogni volta che è necessario (a causa, ad esempio, dei formati di dati esterni) utilizzare tipi di dimensioni esplicite come uint32_t.

1

insidie ​​tipiche per 32bit/64bit porting sono:

L'assunto implicito dal programmatore che sizeof (void *) == 4 * sizeof (char). Se stai facendo questa ipotesi e per es. allocare gli array in questo modo ("Ho bisogno di 20 puntatori per allocare 80 byte"), il codice si interrompe a 64 bit perché causerà sovraccarichi del buffer.

Il "kitten-killer", int x = (int) e qualcosa; (e il contrario, void * ptr = (void *) some_int). Di nuovo un'ipotesi di sizeof (int) == sizeof (void *). Ciò non causa overflow ma perde i dati: il più alto 32 bit del puntatore, ovvero.

Entrambi questi problemi sono di una classe chiamata aliasing di tipo (assumendo identità/intercambiabilità/equivalenza su un livello di rappresentazione binaria tra due tipi) e tali ipotesi sono comuni; come in UN * X, supponendo time_t, size_t, off_t being int, o su Windows, HANDLE, void * e long intercambiabile, ecc ...

Presupposti sulla struttura dei dati/utilizzo dello spazio di stack (Vedi 5. sotto come bene). Nel codice C/C++, le variabili locali sono allocate nello stack e lo spazio utilizzato è diverso tra la modalità a 32 bit e 64 bit a causa del punto in basso, e a causa delle diverse regole per il passaggio degli argomenti (32 bit x86 di solito in pila, 64 bit x86 in parte nei registri). Il codice che fa quasi scappare lo stack predefinito su 32 bit potrebbe causare arresti anomali dello stack over a 64 bit. Questo è relativamente facile da individuare come causa del crash, ma dipende dalla configurabilità dell'applicazione, probabilmente difficile da risolvere.

Differenze di tempo tra codice a 32 bit e 64 bit (a causa di diverse dimensioni di codice/impronte di cache, o caratteristiche/modelli di accesso di memoria diversi o convenzioni di chiamata diverse) potrebbero interrompere le "calibrazioni". Supponi, per (int i = 0; i < 1000000; ++ i) sleep (0); è probabile che avrà tempi diversi per 32 bit e 64 bit ...

Infine, l'ABI (Application Binary Interface). Di solito ci sono differenze maggiori tra gli ambienti a 64 bit e 32 bit rispetto alle dimensioni dei puntatori ... Attualmente esistono due "rami" principali di ambienti a 64 bit, IL32P64 (ciò che usa Win64 - int e long sono int32_t, solo uintptr_t/void * è uint64_t, parlando in termini di numeri interi da) e LP64 (cosa usa UN * X - int è int32_t, long è int64_t e uintptr_t/void * è uint64_t), ma ci sono anche le "suddivisioni" di diverse regole di allineamento - alcuni ambienti assumono long, float o double align alle rispettive dimensioni, mentre altri assumono che si allineano a multipli di quattro byte. In Linux a 32 bit, allineano tutti a quattro byte, mentre in Linux a 64 bit, gli allineamenti float a quattro, lunghi e doppi a multipli di otto byte. La conseguenza di queste regole è che in molti casi, bith sizeof (struct {...}) e l'offset dei membri struttura/classe sono diversi tra ambienti 32bit e 64bit anche se la dichiarazione del tipo di dati è completamente identica. Oltre all'impatto sull'assegnazione di array/vettori, questi problemi influenzano anche i dati in/output, ad es. attraverso i file - se un'app a 32 bit scrive ad es. struct {char a; int b; char c, long d; double e} in un file che la stessa app ha ricompilato per la lettura a 64 bit, il risultato non sarà esattamente quello che si spera. Gli esempi appena forniti riguardano solo le primitive della lingua (char, int, long ecc.), Ma naturalmente influiscono su tutti i tipi di tipi di dati della libreria/runtime della piattaforma, se size_t, off_t, time_t, HANDLE, essenzialmente qualsiasi struttura/unione non banale/class ... - quindi lo spazio per l'errore qui è grande,

E poi ci sono le differenze di livello inferiore, che entrano in gioco ad esempio per il montaggio ottimizzato a mano (SSE/SSE2/...); 32 bit e 64 bit hanno registri diversi (numeri di), diverse regole di passaggio degli argomenti; tutto ciò influisce molto sul rendimento di tali ottimizzazioni ed è molto probabile che ad es. Il codice SSE2 che offre le migliori prestazioni in modalità 32 bit dovrà essere riscritto/deve essere migliorato per offrire la migliore modalità a 64 bit delle prestazioni.

Ci sono anche vincoli di progettazione del codice che sono molto diversi per 32 bit e 64 bit, in particolare per quanto riguarda l'allocazione/gestione della memoria; un'applicazione che è stata accuratamente codificata per "massimizzare l'inferno del mem che può ottenere in 32 bit" avrà una logica complessa su come/quando allocare/memoria libera, uso di file mappati in memoria, cache interna, ecc. essere dannoso in 64 bit in cui si potrebbe "semplicemente" sfruttare l'enorme spazio di indirizzamento disponibile. Una tale app potrebbe ricompilare per 64 bit semplicemente, ma si comporta peggio di qualche "antica versione deprecata semplice" che non ha tutte le ottimizzazioni degli spioncino massimizzabili a 32 bit.

Quindi, in definitiva, si tratta anche di miglioramenti e/o guadagni, ed è lì che più lavoro, in parte in programmazione, in parte in progettazione/requisiti viene in. Anche se la vostra applicazione ricompila modo pulito sia in ambienti 32bit e 64bit e viene verificata su entrambi , sta effettivamente beneficiando di 64 bit? Ci sono cambiamenti che possono/devono essere fatti alla logica del codice per farlo fare di più/correre più velocemente a 64 bit? Puoi fare queste modifiche senza rompere la compatibilità a 32 bit con le versioni precedenti? Senza impatti negativi sull'obiettivo a 32 bit? Dove saranno i miglioramenti e quanto puoi guadagnare? per un grande progetto commerciale, le risposte a queste domande sono spesso indicatori importanti sulla tabella di marcia, perché il punto di partenza è un po 'esistente "macchina da soldi" ...

+1

degno di guardare http://www.viva64.com/en/a/0004/ – Saqlain

Problemi correlati