Поделиться через


Правила использования указателей

Перенос кода для компиляции для 32-разрядной и 64-разрядной версии Microsoft Windows прост. Вам нужно выполнить только несколько простых правил для приведения указателей и использовать новые типы данных в коде. Ниже приведены правила для манипуляции указателем.

  1. Не указывайте указатели на int, long, ULONG или DWORD.

    Если необходимо создать указатель для проверки некоторых битов, задания или очистки битов или управления его содержимым, используйте тип UINT_PTR или INT_PTR . Эти типы представляют собой целые типы, масштабируемые до размера указателя для 32-разрядных и 64-разрядных Windows (например, ULONG для 32-разрядных Windows и _int64 для 64-разрядных Windows). Например, предположим, что вы переносите следующий код:

    ImageBase = (PVOID)((ULONG)ImageBase | 1);

    В рамках процесса переноса код изменится следующим образом:

    ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);

    Используйте UINT_PTR и INT_PTR, если это необходимо (и если вы не уверены, что они необходимы, нет вреда в их использовании только в случае). Не указывайте указатели на типы ULONG, LONG, INT, UINT или DWORD.

    Обратите внимание, что HANDLE определяется как void*, поэтому ввод значения HANDLE в значение ULONG для тестирования, задания или очистки 2 битов с низким порядком является ошибкой в 64-разрядной Windows.

  2. Используйте функцию PtrToLong или PtrToUlong для усечения указателей.

    Если необходимо усечь указатель на 32-разрядное значение, используйте функцию PtrToLong или PtrToUlong (определенную в Basetsd.h). Эти функции отключают предупреждение усечения указателя в течение длительности вызова.

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

  3. Будьте осторожны при использовании POINTER_32 значений в коде, который может быть скомпилирован как 64-разрядный код. Компилятор будет подписывать указатель, когда он назначается собственному указателю в 64-разрядном коде, а не нулевым расширением указателя.

  4. Будьте осторожны при использовании POINTER_64 значений в коде, которые могут быть скомпилированы как 32-разрядный код. Компилятор будет подписывать указатель в 32-разрядном коде, а не нулевым расширением указателя.

  5. Будьте осторожны с использованием параметров OUT.

    Например, предположим, что у вас есть функция, определенная следующим образом:

    void func( OUT PULONG *PointerToUlong );

    Не вызывайте эту функцию следующим образом.

    ULONG ul;
    PULONG lp;
    func((PULONG *)&ul);
    lp = (PULONG)ul;
    

    Вместо этого используйте следующий вызов.

    PULONG lp;
    func(&lp);
    

    Типcasting &ul в PULONG* предотвращает ошибку компилятора, но функция записывает 64-разрядное значение указателя в память по адресу &ul. Этот код работает в 32-разрядной версии Windows, но приведет к повреждению данных в 64-разрядной версии Windows и будет тонким, трудно найти повреждение. В нижней строке: не играйте трюки с кодом C — простой и простой лучше.

  6. Будьте осторожны с полиморфными интерфейсами.

    Не создавайте функции, принимаюющие параметры DWORD для полиморфных данных. Если данные могут быть указателем или целочисленным значением, используйте тип UINT_PTR или PVOID .

    Например, не создавайте функцию, которая принимает массив параметров исключения, типизированных как значения DWORD . Массив должен быть массивом DWORD_PTR значений. Поэтому элементы массива могут содержать адреса или 32-разрядные целочисленные значения. (Общее правило заключается в том, что если исходный тип имеет значение DWORD и он должен быть шириной указателя, преобразуйте его в значение DWORD_PTR . Именно поэтому существуют соответствующие типы точности указателя.) Если у вас есть код, использующий DWORD, ULONG или другие 32-разрядные типы в полиморфном режиме (то есть вы действительно хотите, чтобы элемент параметра или структуры держал адрес), используйте UINT_PTR вместо текущего типа.

  7. Используйте новые функции класса окон.

    Если у вас есть закрытые данные окна или класса, содержащие указатели, коду потребуется использовать следующие новые функции:

    Эти функции можно использовать как в 32-разрядных, так и 64-разрядных Windows, но они необходимы для 64-разрядных Windows. Подготовьтесь к переходу с помощью этих функций.

    Кроме того, необходимо получить доступ к указателям или дескрипторам в частных данных класса, используя новые функции в 64-разрядной версии Windows. Чтобы помочь в поиске этих случаев, следующие индексы не определены в Winuser.h во время 64-разрядной компиляции:

    • GWL_WNDPROC
    • GWL_HINSTANCE
    • GWL_HWNDPARENT
    • GWL_USERDATA

    Вместо этого Winuser.h определяет следующие новые индексы:

    • GWLP_WNDPROC
    • GWLP_HINSTANCE
    • GWLP_HWNDPARENT
    • GWLP_USERDATA
    • GWLP_ID

    Например, следующий код не компилируется:

    SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);

    Его следует изменить следующим образом:

    SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);

    При настройке члена cbWndExtra структуры WNDCLASS обязательно зарезервируйте достаточно места для указателей. Например, если в настоящее время резервируются байты sizeof(DWORD) для значения указателя, размер резерва (DWORD_PTR) байтов.

  8. Доступ ко всем данным окна и класса с помощью FIELD_OFFSET.

    Обычно доступ к данным окна используется с помощью жестко закодированных смещения. Этот метод не переносится в 64-разрядную версию Windows. Чтобы сделать код переносимым, получите доступ к данным окна и класса с помощью макроса FIELD_OFFSET . Не предполагайте, что второй указатель имеет смещение 4.

  9. Размер типов LPARAM, WPARAM и LRESULT изменяется с помощью платформы.

    При компиляции 64-разрядного кода эти типы расширяются до 64 битов, так как обычно они содержат указатели или целые типы. Не смешивайте эти значения с значениями DWORD, ULONG, UINT, INT, int или long. Изучите способ использования этих типов и убедитесь, что непреднамеренно усечение значений.