una sistema redundante redundancia qué provoca problemas minima informatica informacion inconsistencia evitar electrico ejemplos datos como c winapi types history

sistema - Tipos de datos de Windows... ¿por qué tan redundante/indescriptible?



redundancia informatica ejemplos (3)

Cuando los archivos de encabezado de la API de Windows se construyeron por primera vez hace 25 años, un int era de 16 bits y un long 32 bits. Los archivos de encabezado han evolucionado a lo largo del 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 la 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 API de C / C ++ a otros idiomas. Muchas de esas herramientas se escribieron originalmente para funcionar con las definiciones de encabezado actuales (en ese momento). Si Microsoft simplemente cambiara los archivos de encabezado para simplificarlos como usted sugiere, muchas de esas herramientas dejarán de funcionar.

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

Entonces, sí, los archivos de encabezado de Windows son a veces 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.

Información adicional:

Estoy de acuerdo en que a primera vista todas esas definiciones parecen locas. Pero como alguien que ha visto evolucionar los archivos de encabezado de Windows a lo largo del tiempo, entiendo cómo surgieron. La mayoría de esas definiciones tenían mucho sentido cuando fueron introducidas, incluso si ahora parecen locas. En cuanto al caso específico ULONGLONG y DWORD64 , imagino que se agregaron por coherencia, ya que los antiguos archivos de encabezado tenían ULONG y DWORD , por lo que los programadores esperaban los otros dos. En cuanto a por qué se ULONG y DWORD cuando son lo mismo, puedo pensar en varias posibilidades, dos de las cuales son:

  • Un equipo de API usó ULONG y otro usó DWORD , y cuando los archivos de encabezado se consolidaron, mantuvieron ambos en lugar de romper el código convirtiéndolo en uno u otro.
  • Algunos programadores se sienten más cómodos pensando en términos de ULONG que DWORD . ULONG implica un tipo entero en el que puede hacer cálculos matemáticos, mientras que DWORD simplemente 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 desearía modificar.

Su pregunta inicial fue si había algún razonamiento detrás de las definiciones aparentemente locas, o si hay una abstracción que no está perdiendo. La respuesta simple es que las definiciones evolucionaron, y los cambios tienen sentido en ese momento. No hay una abstracción en particular, aunque la intención es que si escribe su código para usar los tipos que se definen 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 un valor de retorno cuando la API dice que el valor de retorno es HANDLE , tendrá problemas.

¿Podría alguien explicar exactamente por qué se han definido los siguientes typedef s / #define s? ¿Qué valor tienen, en comparación con los originales?

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 esto? ¿Es algún tipo de abstracción que no entiendo?

Editar :

Para aquellas personas que mencionan la compatibilidad cruzada del compilador:

Mi pregunta no es sobre por qué no usaron unsigned long long lugar de, por ejemplo, DWORD64 . Mi pregunta es acerca de por qué alguien usaría DWORD64 lugar de ULONG64 (o viceversa)? ¿No es que ambos de estos typedef tienen 64 bits de ancho?

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

Sin embargo, me pregunto por qué usaron ULONGLONG lugar de por long long . ¿Existe alguna diferencia potencial en el significado, que no cubra por long long ni DWORDLONG ?


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

  • Son tipos históricos preservados para compatibilidad con versiones anteriores.
  • son nombres diferentes para el mismo tipo que surgieron de diferentes equipos de desarrolladores (puede ser sorprendentemente difícil para los equipos mantener la coherencia en un proyecto tan grande como Windows)

typedef char CHAR;

La firmeza de char puede variar entre plataformas y compiladores, por lo que esa es una de las razones. Es posible que los desarrolladores originales también hayan 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 la transición a 64 bits, probablemente descubrieron que algunos de sus argumentos DWORD realmente necesitaban tener una longitud de 64 bits, y probablemente le cambiaron el nombre a DWORD64 para que los usuarios existentes de esas API no se confundieran.

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

Este se remonta a los días de 16 bits, cuando había punteros "cercanos" regulares que eran punteros de 16 bits y "lejanos" que eran de 32 bits. El prefijo L en tipos significa "largo" o "lejos", que ahora no tiene sentido, pero en aquellos días, estos probablemente se definieron 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 los cambios futuros. Tenga en cuenta que Windows también se ejecuta en plataformas distintas de x86; puede pensar en una arquitectura donde los números de punto flotante se representaron en un formato no estándar y las funciones de la API se optimizaron para hacer uso de esta representación. Esto podría estar en conflicto con el tipo de datos float de C.


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

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