Типы данных Windows ... почему так избыточно / неописуемо? - PullRequest
17 голосов
/ 28 февраля 2011

Может кто-нибудь, пожалуйста, точно, почему были определены следующие typedef s / #define s? Какую ценность они имеют по сравнению с оригиналами?

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?

И, что лучше всего:

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

В чем причина этого? Это какая-то абстракция, которую я не понимаю?

* * 1010

Редактировать

Для тех, кто упоминает о совместимости компиляторов:

У меня вопрос , а не о том, почему они не использовали unsigned long long вместо, скажем, DWORD64. Мой вопрос о том, почему кто-то использует DWORD64 вместо ULONG64 (или наоборот)? Разве оба из этих typedef не имеют ширину 64 бита?

Или, как еще один пример: даже в «гипотетическом» компиляторе, который должен был обмануть нас во всех отношениях, какая разница будет между ULONG_PTR и UINT_PTR и DWORD_PTR? Разве все эти абстрактные типы данных не означают одно и то же - SIZE_T?

Однако я спрашиваю, почему они использовали ULONGLONG вместо long long - есть ли потенциальная разница в значении, охватываемая ни long long, ни DWORDLONG?

Ответы [ 3 ]

18 голосов
/ 28 февраля 2011

Большинство этих избыточных имен существуют в основном по двум причинам:

  • они являются историческими типами, сохраненными для обратной совместимости
  • они являются разными именами для того же типа, который возник изразличные команды разработчиков (командам может быть на удивление трудно оставаться последовательными в таком огромном проекте, как Windows)

typedef char CHAR;

Подпись char может варьироваться в зависимости от платформы и компилятораЭто одна из причин.Первоначальные разработчики могли бы также оставить это открытым для будущих изменений в кодировках символов, но, конечно, это больше не актуально, так как сейчас мы используем TCHAR для этой цели.


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

Во время движенияв 64-битные, они, вероятно, обнаружили, что некоторые из их DWORD аргументов действительно должны быть длиной в 64 бита, и они, вероятно, переименовали его в DWORD64, чтобы существующие пользователи этих API не были сбиты с толку.

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

Этот период восходит к 16-битным дням, когда были обычные "ближние" указатели, которые были 16-битными, и "дальние" указатели, которые были 32-битными.Префикс L для типов означает «длинный» или «дальний», что сейчас не имеет смысла, но в те времена они, вероятно, были определены следующим образом:

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

Обновление: Что касается FLOAT, UINT и ULONG, это всего лишь примеры "больше абстракции - это хорошо" с учетом будущих изменений.Имейте в виду, что Windows также работает на платформах, отличных от x86, - вы могли бы подумать об архитектуре, в которой числа с плавающей запятой были представлены в нестандартном формате, а функции API были оптимизированы для использования этого представления.Это может вступить в конфликт с типом данных C float.

9 голосов
/ 28 февраля 2011

Когда заголовочные файлы Windows API были впервые созданы 25 лет назад, int составлял 16 бит, а long составлял 32 бита. Заголовочные файлы со временем эволюционировали, отражая изменения в компиляторах и оборудовании.

Кроме того, Microsoft C ++ не единственный компилятор C ++, который работает с заголовочными файлами Windows. Когда Microsoft добавила ключевое слово size_t, не все компиляторы поддержали его. Но они могли бы легко создать макрос, SIZE_T, чтобы выразить его.

Кроме того, существуют (или были) автоматизированные инструменты, которые преобразуют файлы заголовков API из C / C ++ в другие языки. Многие из этих инструментов изначально были написаны для работы с текущими (на тот момент) определениями заголовков. Если бы Microsoft просто изменила заголовочные файлы, чтобы упростить их, как вы предлагаете, многие из этих инструментов перестали бы работать.

По сути, файлы заголовков сопоставляют типы Windows с наименьшим общим знаменателем, чтобы с ними могли работать несколько инструментов. Временами кажется, что это что-то вроде беспорядка, и я подозреваю, что если Microsoft захочет отбросить какое-либо подобие обратной совместимости, они могут уменьшить большую часть беспорядка. Но это сломало бы много инструментов (не говоря уже о большом количестве документации).

Так что, да, заголовочные файлы Windows иногда бывают беспорядочными. Это цена, которую мы платим за эволюцию, обратную совместимость и возможность работать с несколькими языками.

Дополнительная информация:

Я согласен, что на первый взгляд все эти определения кажутся сумасшедшими. Но как тот, кто видел, как заголовочные файлы Windows эволюционировали со временем, я понимаю, как они возникли. Большинство этих определений имело смысл, когда они были представлены, даже если сейчас они выглядят сумасшедшими. Что касается конкретного случая ULONGLONG и DWORD64, я предполагаю, что они были добавлены для согласованности, поскольку старые заголовочные файлы имели ULONG и DWORD, поэтому программисты ожидали двух других. Что касается того, почему ULONG и DWORD были определены, когда они являются одним и тем же, я могу представить несколько возможностей, две из которых:

  • Одна команда API использовала ULONG, а другая - DWORD, и когда заголовочные файлы были объединены, они просто сохраняли оба, а не ломали код путем преобразования в один или другой.
  • Некоторым программистам удобнее думать с точки зрения ULONG, чем DWORD. ULONG подразумевает целочисленный тип, с которым вы можете выполнять математику, тогда как DWORD просто подразумевает общее 32-битное значение некоторого вида, обычно это ключ, дескриптор или другое значение, которое вы не хотите изменять .

Ваш первоначальный вопрос состоял в том, были ли какие-то рассуждения за, казалось бы, безумными определениями, или есть абстракция, которую вы не пропустили. Простой ответ заключается в том, что определения развивались, а изменения имели смысл в то время. Особой абстракции не существует, хотя намерение состоит в том, что если вы напишите свой код для использования типов, определенных в заголовках, то вы должны иметь возможность переносить ваш код с 32-разрядного на 64-разрядный без беда. То есть DWORD будет одинаковым в обеих средах. Но если вы используете DWORD для возвращаемого значения, когда API говорит, что возвращаемое значение равно HANDLE, у вас будут проблемы.

1 голос
/ 28 февраля 2011

Одной из причин является поддержание некоторой переносимости между компиляторами C.

В частности, DWORD64, теоретически вам просто нужно изменить определение DWORD64, чтобы компилировать код на других компиляторах.

...