2011-02-28 10 views
17

Qualcuno potrebbe per piacere esattamente perché sono stati definiti i seguenti typedef s/#define s? Che valore hanno, rispetto agli originali?Tipi di dati di Windows ... perché così ridondanti/non descrittivi?

typedef char CHAR; 
#define CONST const 
typedef float FLOAT; 

typedef unsigned __int64 DWORD64; //A 64-bit "double"-word?! 
typedef ULONGLONG DWORDLONG;  //What's the difference? 

typedef ULONG_PTR DWORD_PTR;  //What's the difference? 
typedef long LONG_PTR;   //Wasn't INT_PTR enough? 

typedef signed int LONG32;  //Why not "signed long"? 
typedef unsigned int UINT;  //Wait.. UINT is "int", "LONG" is also int? 
typedef unsigned long ULONG;  //ULONG is "long", but LONG32 is "int"? what? 

typedef void *PVOID;    //Why not just say void*? 
typedef void *LPVOID;    //What?! 

typedef ULONG_PTR SIZE_T;   //Why not just size_t? 

E, meglio di tutti:

#define VOID void     //Assuming this is useful (?), why not typedef? 

Qual è il ragionamento che sta dietro questi? È una specie di astrazione che non capisco?


Edit:

Per quelle persone che citano il compilatore cross-compatilibity:

La mia domanda non è sul perché non hanno usato unsigned long long invece di, diciamo, DWORD64. La mia domanda riguarda il motivo per cui qualcuno dovrebbe utilizzare DWORD64 anziché ULONG64 (o viceversa)? Non sono entrambi quelli typedef ed hanno una larghezza di 64 bit?

Oppure, come un altro esempio: Anche in un compilatore "ipotetico" che doveva ingannarci a tutti gli effetti, quale sarebbe la differenza tra ULONG_PTR e UINT_PTR e DWORD_PTR? Non tutti questi tipi di dati astratti significano solo la stessa cosa - SIZE_T?

Tuttavia, io sono chiedendo perché hanno usato ULONGLONG invece di long long - v'è alcuna differenza di potenziale di significato, né coperto da long longDWORDLONG?

+1

Sospetterei molto di questi harken dai tempi bui di win16, ma lo lascerò a quelli che in realtà hanno scritto il codice di Windows nei secoli del passato per rispondere :) – bdonlan

+0

@bdonlan: Haha okay. :) – Mehrdad

+0

Ci sono molte cose che influenzano la storia di questi tipi. Una volta definito, non puoi mai riprenderlo. #define Void vuoto è stato fatto, perché originariamente void * non era originariamente nello standard C (non riesce a trovare un collegamento ...). Quindi alcuni compilatori non l'hanno riconosciuto. Ma se lo rimuovi ora, rompi il codice. E nessuno vuole che il suo codice si rompa, quando aggiorna l'SDK. Ci sono già troppi problemi durante l'aggiornamento, perché interromperlo in qualche vecchio modulo che non è stato toccato per "secoli", ma ora improvvisamente non viene più compilato. – Christopher

risposta

18

La maggior parte di questi nomi ridondanti esistono principalmente per due motivi:

  • stanno tipi storici conservati per compatibilità
  • sono nomi diversi per lo stesso tipo che nasce da diverse squadre di sviluppatori (può essere sorprendentemente difficile per le squadre di rimanere coerente in un grande progetto come Windows)

typedef char CHAR; 

La firma di char può variare tra piattaforme e compilatori, quindi questa è una delle ragioni. Gli sviluppatori originali avrebbero potuto tenerlo aperto anche per future modifiche alle codifiche dei caratteri, ma ovviamente questo non è più rilevante dato che ora utilizziamo lo TCHAR per questo scopo.


typedef unsigned __int64 DWORD64; //A 64-bit "double"-word?! 

Durante il passaggio a 64-bit, probabilmente scoperto che alcuni dei loro DWORD argomenti realmente necessarie per essere di 64 bit di lunghezza, e probabilmente rinominato DWORD64 in modo che gli attuali utenti di tali API weren' t confuso.


Questo risale ai giorni 16 bit, quando c'erano regolari puntatori "vicino", che erano a 16 bit e "lontano" puntatori che erano a 32 bit. La L prefisso sui tipi di sta per "lungo" o "lontano", che non ha senso adesso, ma in quei giorni, questi sono stati probabilmente definiti in questo modo:

typedef void near *PVOID; 
typedef void far *LPVOID; 

Aggiornamento: Per quanto riguarda FLOAT , UINT e ULONG, questi sono solo esempi di "più astrazione è buona", in vista di cambiamenti futuri. Tieni presente che Windows viene eseguito anche su piattaforme diverse da x86: potresti pensare a un'architettura in cui i numeri in virgola mobile sono stati rappresentati in un formato non standard e le funzioni dell'API sono state ottimizzate per utilizzare questa rappresentazione. Ciò potrebbe quindi essere in conflitto con il tipo di dati float di C.

+0

+1 Ottima spiegazione! Che dire di 'FLOAT',' UINT' e 'ULONG'? Perché non usare solo 'float',' unsigned int' e 'unsigned long'? (Sono 'UINT' e' ULONG' a dimensione fissa?) Oh, ed è 'CHAR' firmato o non firmato? – Mehrdad

+0

@ Mehrdad: vedere il mio aggiornamento. Ora che lo chiedi, non sono così sicuro della firma di 'CHAR' - la mia risposta è piena di ipotesi, ma è il meglio che posso fare. – casablanca

+1

@casablanca: Er ... come è 'INT'" più astrazione "di' int'? E, potresti per favore dare un esempio di come l'introduzione di 'FLOAT' * potrebbe * essere utile su piattaforme e/o compilatori, anche in un senso remoto, teorico? – Mehrdad

1

Uno dei motivi è il mantenimento di una sorta di portabilità tra i compilatori C.

In particolare DWORD64, in teoria, è sufficiente modificare la definizione di DWORD64 per ottenere la compilazione del codice su altri compilatori.

+0

Perché non usare 'ULONG64' dappertutto? E per il resto, come "VOID"? (Stavo chiedendo sia la ridondanza nelle definizioni * e * nel numero di typedef ... c'è qualche ragione, anche in teoria, per usare 'DWORD64' invece di' ULONG64'?) – Mehrdad

+1

Diversi compilatori hanno "costruito" diverso in "nomi per interi a 64 bit. Il typedef VOID sembra un po 'inutile - non può spiegarlo. – Jimmy

+0

@Jimmy: La mia domanda non riguarda il motivo per cui non hanno usato "unsigned long long"; la mia domanda riguarda perché qualcuno dovrebbe usare 'DWORD64' invece di' ULONG64' (o viceversa)? Non * entrambi * di quelli 'typedef'ed per essere 64 bit? – Mehrdad

9

Quando i file di intestazione dell'API di Windows sono stati creati 25 anni fa, uno int era di 16 bit e uno long era di 32 bit. I file di intestazione si sono evoluti nel tempo per riflettere i cambiamenti nei compilatori e nell'hardware.

Inoltre, Microsoft C++ non è l'unico compilatore C++ che funziona con i file di intestazione di Windows. Quando Microsoft ha aggiunto la parola chiave size_t, non tutti i compilatori lo hanno supportato. Ma potrebbero facilmente creare una macro, SIZE_T, per esprimerla.

Inoltre, esistono (o sono stati) strumenti automatici che convertono i file di intestazione API da C/C++ in altre lingue. Molti di questi strumenti sono stati originariamente scritti per funzionare con le attuali definizioni di intestazione (al momento). Se Microsoft dovesse semplicemente modificare i file di intestazione per semplificarli come suggerito, molti di questi strumenti smetterebbero di funzionare.

Fondamentalmente, i file di intestazione mappano i tipi di Windows a un minimo comune denominatore in modo che più strumenti possano lavorare con loro. A volte sembra essere un casino, e sospetto che se Microsoft fosse disposta a rinunciare a qualsiasi parvenza di compatibilità con le versioni precedenti, potrebbe ridurre una gran parte del caos. Ma così facendo si rompono molti strumenti (per non parlare di molta documentazione).

Quindi, sì, i file di intestazione di Windows sono a volte un disastro. Questo è il prezzo che paghiamo per l'evoluzione, la retrocompatibilità e la capacità di lavorare con più lingue.

Ulteriori informazioni:

Sono d'accordo che a prima vista tutte queste definizioni sembrano impazzire. Ma come colui che ha visto evolversi i file di intestazione di Windows nel tempo, capisco come sono arrivati. La maggior parte di quelle definizioni ha perfettamente senso quando sono state introdotte, anche se ora sembrano pazze. Per quanto riguarda il caso specifico ULONGLONG e DWORD64, immagino che siano stati aggiunti per coerenza, poiché i vecchi file di intestazione avevano ULONG e DWORD, quindi i programmatori si aspettavano gli altri due. Per quanto riguarda perché ULONG e DWORD sono stati entrambi definiti quando sono la stessa cosa, mi viene in mente diverse possibilità, due dei quali sono:

  • Una squadra API utilizzata ULONG e un altro usato DWORD, e quando i file di intestazione sono stati consolidati hanno semplicemente mantenuto sia il codice piuttosto che la rottura convertendosi all'uno o all'altro.
  • Alcuni programmatori sono più a loro agio in termini di ULONG rispetto a DWORD. ULONG implica un tipo intero su cui è possibile eseguire operazioni matematiche, mentre DWORD implica semplicemente un valore generico a 32 bit di qualche tipo, in genere qualcosa che è una chiave, un handle o un altro valore che non si desidera modificare.

La tua domanda iniziale era se ci fosse qualche ragionamento dietro le definizioni apparentemente folli, o se c'è un'astrazione che non ti manca. La semplice risposta è che le definizioni si sono evolute, con i cambiamenti che avevano un senso al momento. Non c'è un'astrazione particolare, anche se l'intento è che se si scrive il codice per utilizzare i tipi definiti nelle intestazioni, è possibile che sia in grado di eseguire il porting del codice da 32-bit a 64-bit senza problemi. Cioè, DWORD sarà lo stesso in entrambi gli ambienti. Ma se si utilizza DWORD per un valore restituito quando l'API dice che il valore restituito è HANDLE, si avranno problemi.

+0

Grazie per la risposta, ma per favore vedi la mia modifica.La mia domanda non riguarda il motivo per cui non hanno usato i tipi di dati incorporati (che ovviamente variano dal compilatore ... tranne per 'void', che mi lascia sconcertato), ma perché hanno più nomi per lo stesso tipo di dati astratto , come 'DWORD64' e' ULONG64' per "un intero con una larghezza di 64 bit". Perché un ** cliente ** sceglie 'ULONG_PTR' su' UINT_PTR', o 'DWORDLONG' su' ULONGLONG' o 'DWORD64' o' ULONG64'? – Mehrdad

+0

@ Mehrdad - perché non c'è nulla nello standard che dice quanto è grande un lungo (a parte deve essere grande almeno quanto un int). DWORD64 è a 64 bit su qualsiasi cosa, da una finestra a 16 bit a un Cray a 128 bit. –

+0

@Marting: Wait, ma qual è la differenza tra 'ULONG64' e' ULONGLONG'? Se 'ULONG64' è sempre a 64 bit, allora perché non usare' DWORD64'? E se * non * è sempre a 64 bit, allora perché non usare 'ULONGLONG'? – Mehrdad