2011-02-28 13 views
17

¿Podría alguien decir exactamente por qué se han definido los siguientes typedef s/#define s? ¿Qué valor tienen, en comparación con los originales?Tipos de datos de Windows ... ¿por qué tan redundante/indescriptible?

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? 

Y, lo mejor de todo:

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

¿Cuál es el razonamiento detrás de estos? ¿Es algún tipo de abstracción que no entiendo?


Editar:

Para aquellas personas que citan compilador cruzado-compatilibity:

Mi pregunta es no acerca de por qué no utilizaron unsigned long long en lugar de, digamos, DWORD64. Mi pregunta es por qué alguien usaría DWORD64 en lugar de ULONG64 (o viceversa)? ¿No están ambos de esos typedef ed ser de 64 bits de ancho?

O, como otro ejemplo: Incluso en un compilador "hipotético" que estaba destinado a engañarnos en todos los aspectos, ¿cuál sería la diferencia entre ULONG_PTR y UINT_PTR y DWORD_PTR? ¿No son esos todos los tipos de datos abstractos que significan lo mismo - SIZE_T?

Sin embargo, yo soy preguntando por qué se utilizan en lugar de ULONGLONGlong long - ¿hay alguna diferencia de potencial en el significado, ni cubierto por long long ni DWORDLONG?

+1

Sospecho que muchos de estos harken de las edades oscuras de win16, pero voy a dejar que aquellos que realmente escribieron código de Windows en las edades de antaño respondan :) – bdonlan

+0

@bdonlan: Haha bien. :) – Mehrdad

+0

Hay muchas cosas que influyen en la historia de estos tipos. Cuando una vez definido, nunca puede recuperarlo. #define VOID void se realizó porque originalmente void * originalmente no estaba en el estándar C (no se puede encontrar un enlace ...). Entonces algunos compiladores no lo reconocieron. Pero si lo quitas ahora, rompes el código. Y nadie quiere romper su código cuando actualiza el SDK. Ya hay demasiados problemas al actualizar, ¿por qué romper esto en algún módulo anterior que no se tocó durante "siglos" pero ahora de repente ya no se compila? – Christopher

Respuesta

18

La mayoría de estos nombres redundantes existen principalmente por dos razones:

  • que están tipos históricos preservados para mantener la compatibilidad
  • son diferentes nombres para el mismo tipo que surgió de diferentes equipos de desarrolladores (puede ser sorprendentemente difícil para los equipos que se mantienen consistentes a través de un proyecto de gran envergadura como Windows)

typedef char CHAR; 

La firma de char puede variar entre plataformas y compiladores, por eso esa es una razón. Los desarrolladores originales también podrían haber mantenido esto abierto para futuros cambios en las codificaciones de caracteres, pero por supuesto esto ya no es relevante ya que usamos TCHAR ahora para ese propósito.


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

Durante el paso a 64 bits, es probable que descubrieron que algunos de sus DWORD argumentos realmente necesarios para ser de 64 bits de largo, y probablemente renombraron DWORD64 para que los usuarios existentes de esas API no eran' t confundido


Ésta data de nuevo a los días de 16 bits, cuando había regulares punteros "cerca", que eran punteros "lejos" que eran de 32 bits y de 16 bits. El L prefijo de tipos significa "larga" o "lejos", que no tiene sentido ahora, pero en aquellos días, estos fueron probablemente definen así:

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

Actualización: En cuanto a FLOAT , UINT y ULONG, estos son solo ejemplos de "más abstracción es buena", en vista de cambios futuros. Tenga en cuenta que Windows también se ejecuta en plataformas que no sean x86; podría pensar en una arquitectura donde los números de coma flotante se representaran en un formato no estándar y las funciones de la API se hayan optimizado para hacer uso de esta representación. Esto podría estar en conflicto con el tipo de datos float de C.

+0

+1 ¡Gran explicación! ¿Qué pasa con 'FLOAT',' UINT', y 'ULONG'? ¿Por qué no usar '' float', 'unsigned int' y' unsigned long'? (¿Están 'UINT' y' ULONG' de tamaño fijo?) Ah, y ¿'CHAR 'está firmado o no, entonces? – Mehrdad

+0

@Mehrdad: ver mi actualización. Ahora que lo preguntas, no estoy tan seguro de la firma de 'CHAR': mi respuesta está llena de conjeturas, pero es lo mejor que puedo hacer. – casablanca

+1

@casablanca: Er ... ¿cómo es 'INT'" más abstracción "que' int'? ¿Podría dar un ejemplo de cómo la introducción de 'FLOAT' * podría * ser útil en plataformas y/o compiladores, incluso en un sentido teórico remoto? – Mehrdad

1

Una razón es mantener algún tipo de portabilidad entre los compiladores C.

En particular DWORD64, en teoría solo necesita cambiar la definición de DWORD64 para obtener el código compilando en otros compiladores.

+0

¿Por qué no usar 'ULONG64' en todas partes? ¿Y el resto, como 'VOID'? (Preguntaba sobre la redundancia en las definiciones * y * en el número de typedefs ... ¿hay alguna razón, incluso en teoría, para usar 'DWORD64' en lugar de' ULONG64'?) – Mehrdad

+1

Diferentes compiladores tienen diferentes "compilaciones" en "nombres para enteros de 64 bits". Sin embargo, la definición de tipo VOID parece un tanto inútil; no puedo explicarlo. – Jimmy

+0

@Jimmy: Mi pregunta no es sobre por qué no usaron 'unsigned long long'; mi pregunta es por qué alguien usaría 'DWORD64' en lugar de' ULONG64' (o viceversa)? ¿No son * ambos * de esos 'typedef'ed para ser 64 bits? – Mehrdad

9

Cuando los archivos de encabezado de la API de Windows se crearon por primera vez hace 25 años, un int era de 16 bits y un long tenía 32 bits. Los archivos de encabezado han evolucionado con el tiempo para reflejar los cambios en los compiladores y en el hardware.

Además, Microsoft C++ no es el único compilador de C++ que funciona con los archivos de encabezado de Windows. Cuando Microsoft agregó la palabra clave size_t, no todos los compiladores lo admitieron. Pero podrían crear fácilmente una macro, SIZE_T, para expresarla.

Además, hay (o hubo) herramientas automatizadas que convierten los archivos de encabezado de la API de C/C++ a otros idiomas. Muchas de esas herramientas se escribieron originalmente para trabajar con las definiciones de encabezado actuales (en ese momento). Si Microsoft acabara de cambiar los archivos de encabezado para simplificarlos como sugiere, muchas de esas herramientas dejarían de funcionar.

Básicamente, los archivos de encabezado asignan los tipos de Windows a un mínimo común denominador para que varias herramientas puedan trabajar con ellos. A veces parece ser un desastre, y sospecho que si Microsoft estuviera dispuesto a rechazar cualquier apariencia de compatibilidad con versiones anteriores, podrían reducir una gran parte del problema. Pero hacerlo rompería muchas herramientas (sin mencionar mucha documentación).

Así que, sí, los archivos de encabezado de Windows a veces son un desastre. Ese es el precio que pagamos por la evolución, la compatibilidad con versiones anteriores y la capacidad de trabajar con varios idiomas.

Otros detalles:

estoy de acuerdo que a primera vista todas esas definiciones parecen locos. Pero como uno que ha visto los archivos de encabezado de Windows evolucionan con el tiempo, entiendo cómo surgieron. La mayoría de esas definiciones tenían mucho sentido cuando se las presentaba, incluso si ahora se ven locas. En cuanto al caso específico ULONGLONG y DWORD64, imagino que se agregaron por coherencia, ya que los archivos de encabezado anteriores tenían ULONG y DWORD, por lo que los programadores esperarían los otros dos. En cuanto a por qué ULONG y DWORD fueron ambos definidos cuando son la misma cosa, yo puedo pensar en varias posibilidades, dos de los cuales son:

  • Un equipo de la API utiliza ULONG y otro utilizó DWORD, y cuando los archivos de cabecera se consolidaron simplemente mantuvieron ambos en lugar de descifrar el código convirtiéndolo en uno u otro.
  • Algunos programadores están más cómodos pensando en términos de ULONG que DWORD. ULONG implica un tipo entero con el que puede realizar operaciones matemáticas, mientras que DWORD solo implica un valor genérico de 32 bits de algún tipo, generalmente algo que es una clave, un identificador u otro valor que no desea modificar.

Su pregunta inicial era si había algún razonamiento detrás de las definiciones aparentemente locas, o si hay una abstracción que no se está perdiendo. La respuesta simple es que las definiciones evolucionaron, con los cambios teniendo sentido en ese momento. No hay una abstracción particular, aunque la intención es que si escribe su código para usar los tipos que están definidos en los encabezados, entonces debería poder portar su código de 32 bits a 64 bits sin problemas. Es decir, DWORD será el mismo en ambos entornos. Pero si usa DWORD para obtener un valor de retorno cuando la API dice que el valor devuelto es HANDLE, tendrá problemas.

+0

Gracias por la respuesta, pero por favor vea mi edición.Mi pregunta no es sobre por qué no usaron tipos de datos integrados (que obviamente varían según el compilador ... excepto 'void', eso me desconcierta), pero ¿por qué tienen varios nombres para el mismo tipo de datos abstractos? , como 'DWORD64' y' ULONG64' para "un entero con un ancho de 64 bits". ¿Por qué un ** cliente ** elegirá 'ULONG_PTR' sobre' UINT_PTR', o 'DWORDLONG' sobre' ULONGLONG' o 'DWORD64' o' ULONG64'? – Mehrdad

+0

@ Mehrdad, porque no hay nada en el estándar que indique qué tan grande es una longitud larga (aparte de que debe ser al menos tan grande como una int). DWORD64 tiene 64 bits en cualquier cosa, desde una ventana de 16 bits a una Cray de 128 bits –

+0

@Marting: Espera, pero entonces ¿cuál es la diferencia entre 'ULONG64' y' ULONGLONG'? Si 'ULONG64' es siempre de 64 bits, ¿por qué no usar' DWORD64'? Y si * no * siempre son 64 bits, ¿por qué no usar 'ULONGLONG'? – Mehrdad

Cuestiones relacionadas