Подтвердить что ты не робот

Типы данных Windows... почему так избыточно/undescriptive?

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

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

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

typedef ULONG_PTR DWORD_PTR;      //What 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?

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


Edit

Для тех людей, которые упоминают кросс-совместимость компилятора:

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

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

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

4b9b3361

Ответ 1

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

  • они являются историческими типами, сохраненными для обратной совместимости.
  • это разные имена для того же типа, которые возникли у разных групп разработчиков (для команд может быть удивительно сложно оставаться последовательным в таком огромном проекте, как 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.

Ответ 2

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

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

Кроме того, существуют (или были) автоматизированные инструменты, которые преобразуют файлы заголовков API из 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, у вас возникнут проблемы.

Ответ 3

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

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