• Нынешнее состояние Win64
  • Поддержка процессоров
  • Поддержка Windows
  • Поддержка сторонних компаний
  • Обзор 64-разрядной архитектуры
  • Необходимость в 64-битовой адресации
  • Опыт UNIX
  • Опыт перехода от 16-разрядных версий Windows к 32-разрядным
  • Надолго ли хватит 64 бит?
  • Модель программирования Win64
  • Цели
  • Типы данных
  • Типы данных фиксированной точности
  • Типы данных, соответствующие точности указателей
  • Пример: использование указательных типов данных
  • Различия между Windows и UNIX
  • Перенос имеющегося программного кода
  • Изменения, связанные с использованием API
  • Изменения, связанные с устранением неявных допущений относительно предполагаемых размеров элементов данных
  • Пример: перенос программы sortMM (программа 5.5)
  • Использование предупреждающих сообщений компилятора
  • Код до подготовки к переносу
  • Предупреждающие сообщения компилятора
  • Предупреждающие сообщения и необходимые изменения, касающиеся других программ
  • Резюме
  • Дополнительная литература
  • Упражнения
  • ГЛАВА 16

    Программирование в среде Win64

    Наиболее заметный прогресс в развитии возможностей Windows после появления Windows NT и Windows 95 связан с приходом 64-разрядного программирования и расширением Win32 до Win64. На объединенный API обычно ссылаются просто как на Windows API, и именно такой практики мы придерживались на протяжении всей книги. API Win64 обеспечивает возможность выполнения в Windows наиболее крупных и требовательных в отношении ресурсов приложений уровня предприятий и приложений для научных расчетов. 64-разрядные системы позволяют программам использовать гигантские адресные пространства, которые выходят далеко за предел 4 Гбайт, обусловленный 32-битовой адресацией.

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

    Нынешнее состояние Win64

    В данном разделе анализируется состояние поддержки компанией Microsoft интерфейса Win64 на различных системах и процессорах, сложившееся к концу первого полугодия 2004 года. Поскольку ситуация постоянно меняется, приведенную ниже информацию следует рассматривать лишь в качестве "моментального снимка" реального положения дел. Тем не менее, на охватываемых здесь аспектах программирования эволюция поддержки Win64 никак не сказывается.

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

    Поддержка процессоров

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

    • Семейство процессоров Itanium (Itanium Processor family, IPF) компании Intel, архитектура которых полностью отличается от известной архитектуры Intel x86. IPF предоставляет большие регистровые файлы (включающие 128 регистров общего назначения), каналы многоадресных команд, встроенные трехуровневые кэши, а также множество других средств, обеспечивающих высокую производительность и 64-битовую адресацию. В настоящее время на рынок поставляются процессоры Itanium 2, и хотя их предшественник — процессор Itanium — является теперь уже устаревшим, нам будет удобно ссылаться на все семейство просто как на "процессоры Itanium".

    • Процессоры Opteron и Athlon 64 (AMD64) компании AMD, предназначенные, соответственно, для серверов и рабочих станций. Архитектуру AMD64 можно рассматривать как расширение архитектуры Intel x86, допускающее 64-битовую виртуальную адресацию и параллельное выполнение 32– и 64-битовых операций.

    • 32/64-разрядные процессоры компании Intel, сравнимые с процессорами AMD64. Во время написания этой книги ожидалось, что технология 64-разрядного расширения будет применена в первую очередь в процессорах Xeon. Как и прогнозировалось, такие процессоры появились на рынке в конце 2004 года.

    Поддержка Windows

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

    • Windows XP 64-bit Edition доступна в виде, по крайней мере, двух версий. Бета-версия компании Microsoft поддерживает только процессор AMD Opteron. Компания Hewlett Packard выводит на рынок несколько моделей рабочих станций на базе процессоров Itanium с уже установленной системой Windows XP-Itanium2.

    • Windows Server 2003 Enterprise Edition for 64-bit Extended Systems в настоящее время также проходит бета-тестирование. Эта версия обеспечивает поддержку процессоров AMD Opteron и Intel Xeon с использованием технологии 64-разрядного расширения.

    • Windows Server 2003 Enterprise Edition for 64-bit Itanium-based Systems поддерживает, как говорит само ее название, серверы и рабочие станции, использующие один или несколько процессоров Itanium. Существует также версия Datacenter Edition. Например, эта версия устанавливается на системах Integrity компании Hewlett Packard, которые в настоящее время также появляются на рынке.

    Поддержка сторонних компаний

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

    Обзор 64-разрядной архитектуры

    С точки зрения программиста основная трудность при переходе от 32-разрядной модели к 64-разрядной заключается в том, что размер указателей и таких системных типов данных, как size_t и time_t, теперь может составлять 64 бита. Поэтому виртуальное адресное пространство процесса уже не ограничивается 4 Гбайт (фактически доступны приложениям только 3 Гбайт). Таким образом, перенос программ из Win32 в Win64 по существу требует лишь "удлинения" указателей, с чем связаны лишь самые минимальные последствия для пользовательских данных в модели Windows.

    Необходимость в 64-битовой адресации

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

    • Приложения для обработки изображений. Системы, использующие адресные пространства размером 4 Гбайт, в состоянии обеспечить лишь 20-секундное воспроизведение телевизионного изображения высокой четкости (High-Definition Television, HDTV) в реалистичных цветах.

    • Автоматизированное проектирование механических (Mechanical Computer-Aided Design, MCAD) и электронных (Electronic Computer-Aided Design, ECAD) устройств. Для проектирования сложных сборочных узлов требуется наличие более 3 Гбайт памяти, а проектирование микросхем предъявляет к памяти несоизмеримо более высокие требования.

    • Базы данных и хранилища данных. Использование файлов с размерами в несколько сотен Гбайт не является чем-то необычным, и возможность доступа к виртуальным адресным пространствам сопоставимых размеров значительно упрощает обработку таких файлов.

    Теперь поддержка подобных запросов в отношении адресных пространств большого объема стала реальностью. Пройдет совсем немного времени, и 64-разрядные микропроцессоры станут доступными каждому, а большие объемы физической памяти при разумной стоимости будут поддерживаться на многих системах.

    Потребность в 64-битовой адресации диктуется теми же факторами, которые делают столь желательными и необходимыми файлы гигантских размеров (свыше 4 Гбайт), и теперь, когда имеются достаточно мощные микропроцессоры Itanium, AMD64, а также процессоры, использующие технологию 64-разрядного расширения, вполне естественно ожидать, что Windows должна будет эволюционировать для удовлетворения этих запросов. Использование 64-разрядных ОС существенно в тех случаях, когда Windows отводится заметная роль в прикладных корпоративных и профессиональных системах.

    Тем не менее, многие 32-разрядные приложения смогут работать нормально и на новой платформе, и на первом этапе для их переноса не надо будет ничего предпринимать. Для таких персональных приложений, как Microsoft Office или Adobe PageMaker, в течение некоторого времени переход к 64-битовой адресации, по-видимому, не потребуется. Следовательно, Windows будет поддерживать обратную совместимость.

    Как и следовало ожидать, применение существующих 64-разрядных процессоров часто обеспечивает выигрыш в производительности, но этот выигрыш непосредственно никак не сказывается на программировании на уровне исходного кода.

    Опыт UNIX

    PC-системы всегда отставали от универсальных вычислительных систем (мэйнфреймов) и систем на основе UNIX в том, что касается базовых функциональных возможностей и масштабируемости. То же самое остается справедливым и в случае 64-разрядных архитектур.

    • Основные поставщики UNIX-систем предоставляют 48– и 64-разрядные микропроцессоры с начала 90-х годов прошлого столетия.

    • Основные поставщики UNIX-систем поддерживают 64-разрядные API на протяжении примерно того же периода времени.

    • Сообщество пользователей UNIX остановилось на выборе в качестве стандарта так называемой модели LP64, отличающейся от модели Р64, принятой в Win64, о чем далее еще будет говориться.

    • Переходы от 32 к 64 битам всегда осуществлялись сравнительно простым, если не сказать — тривиальным образом, и можно ожидать, что то же самое будет наблюдаться и при переходе от Win32 к Win64.

    Опыт перехода от 16-разрядных версий Windows к 32-разрядным

    Переход от 16-разрядных версий Windows к 32-разрядным начался в начале 90-х годов прошлого столетия с появлением Windows NT, и набрал ускорение после того, как использование Windows 95 стало обычным делом. Каким бы соблазнительным ни казалось предположение о том, что нас ожидает повторение той же истории, рассматриваемые нами ситуации отличаются в нескольких аспектах.

    • Windows NT и Windows 95 были первыми из широко используемых "реальных" операционных систем для PC в том смысле, что обе системы поддерживали обмен страницами по запросу, потоки, вытесняющую многозадачность и множество других возможностей, которые были описаны в главе 1.

    • Хотя API Win32 значительно расширил полезное адресное пространство, что делает и Win64, усовершенствования этим не ограничивались. Неуклюжие и устаревшие, несмотря на свою популярность, модели расширенной памяти были заменены другими. Аналогичная модель расширенной памяти (не описывается в данной книге) была введена и в Windows 2000, однако общие последствия этого шага в данном случае были не столь существенными.

    • В API Win32 было введено множество новых функциональных возможностей, чего нельзя сказать о Win64.

    Надолго ли хватит 64 бит?

    Что касается мира PC, в котором возникла Windows, то можно утверждать, что первоначальная 16-разрядная модель Intel x86 (фактическое адресное пространство которой является 20-битовым) просуществовала в течение более десяти лет, и столько же времени уже существует и 32-разрядная архитектура. Однако переход к Win64 и 64-разрядному программированию, вообще говоря, происходит медленнее, чем происходил аналогичный переход к 32 битам. Вместе с тем, в обоих случаях переход миникомпьютеров и серверов на следующий уровень осуществлялся, по крайней мере, за 10 лет до того, как это начинало происходить с PC. Тогда вполне естественно задаться вопросом о том, следует ли ожидать перехода серверов или PC к 128 битам в будущем. Берусь утверждать, что любое расширение такого рода произойдет не раньше, чем через 10 лет, исходя из одной лишь величины 64-разрядного адресного пространства.

    Предсказания — вещь ненадежная, однако, воспринимая это серьезно лишь наполовину, можно напомнить о часто цитируемом законе Мура, согласно которому отношение "стоимость/производительность" уменьшается вдвое каждые 18 месяцев. В свою очередь, быстродействие и емкость устройств каждые 18 месяцев примерно удваиваются. Применяя эти рассуждения к адресному пространству, можно ожидать, что дополнительный бит адреса нам будет требоваться через каждые 18 месяцев, откуда следует, что 64-разрядная модель будет исправно служить еще целых 48 лет (то есть почти столько же времени, сколько насчитывает вся история современных компьютеров). Оправданы ли такие неформальные выводы, которые встретились мне в одном из официальных источников, покажет время, однако в прошлом запросы к ресурсам PC возрастали гораздо быстрее, чем утверждается в приведенном прогнозе.

    Модель программирования Win64

    В зависимости от выбора способа представления таких стандартных типов данных С, как указатели и целочисленные типы данных (long, int и short), a также от того, вводятся или не вводятся нестандартные типы данных, возможны несколько вариантов модели 64-разрядного программирования. Напомним, что в стандарте ANSI С размеры типов данных не определяются строго, хотя и требуется, чтобы размер данных типа long int был не меньше размера данных типа int, а размер данных типа int был не меньше размера данных типа short int.

    Цели

    Цель состоит в том, чтобы ввести единое определение Windows API (то есть, общее для Win32 и Win64), благодаря чему можно будет использовать единый базовый исходный код. Использование этого единого определения может потребовать внесения некоторые изменений в исходный код, но эти изменения должны быть сведены к минимуму.

    Microsoft выбрала модель LLP64 (целые числа типа long и 64-битовые указатели), на которую обычно ссылаются просто как на модель Р64. В частности, существуют следующие определения типов данных, применимые как к данным со знаком, так и к данным без знака:

    • char — 8 бит, и wchar — 16 бит.

    • short — 16 бит.

    • int — 32 бита.

    • long int — также 32 бита.

    • Размер указателя любого типа, например PVOID, составляет 64 бита.

    Для тех случаев, когда требуются данные строго определенного размера, предусмотрены дополнительные типы данных. Так, компилятор Microsoft различает следующие типы данных: _int16, _int32 и _int64.

    Типы данных

    Приведенные в этой главе таблицы взяты непосредственно из оперативной справочной системы и представляют единую модель данных Windows (Windows Uniform Data Model). Определения типов можно найти в заголовочном файле BASETSD.H, входящем в состав интегрированной среды разработки приложений Microsoft Visual Studio .NET (версия 7.0) и версию 6.0 этой системы.

    Типы данных фиксированной точности

    Обозначения типов данных фиксированной точности получаются из обычных обозначений типов данных Win32, таких как DWORD или LONG, добавлением суффикса размера, как показано в табл. 16.1.


    Таблица 16.1. Типы данных фиксированной точности

    Тип данных Описание
    DWORD32 32-битовое целое без знака
    DWORD64 64-битовое целое без знака
    INT32 32-битовое целое со знаком
    INT64 64-битовое целое со знаком
    LONG32 32-битовое целое со знаком
    LONG64 64-битовое целое со знаком
    UINT32 Целое типа INT32 без знака
    UINT64 Целое типа INT64 без знака
    ULONG32 Целое типа LONG32 без знака
    ULONG64 Целое типа LONG64 без знака

    Типы данных, соответствующие точности указателей

    Процитируем выдержку из статьи Microsoft под названием "The New Data Types" (доступна на Web-сайте компании Microsoft): "Точность этих типов данных отражает изменение точности указателей (то есть, они становятся 32-битовыми в коде Win32 и 64-битовыми в коде Win64). Поэтому приведение указателей к одному из этих типов при выполнении арифметических операций с указателями является безопасным; при 64-битовой точности указателей размер данных этого типа будет составлять 64 бита. Также и типы данных, соответствующие счетчикам, отражают максимальный размер данных, на которые может ссылаться указатель." Таким образом, эти типы данных обеспечивают автоматическое изменение размеров целочисленных типов данных в зависимости от изменения размеров указателей, в связи с чем их иногда называют полиморфными (polymorphic data types) или платформо-масштабируемыми (platform scaled data types) типами данных. Типы данных, соответствующие точности указателей, перечислены в табл. 16.2, взятой из той же статьи.

    Наиболее важным из них является тип данных SIZE_T, который уже использовался нами при описании размеров блоков памяти в главе 5.

    Наконец, заметьте, что в Win64 размер данных типа HANDLE составляет 64 бита.


    Таблица 16.2. Типы данных, соответствующие точности указателей

    Тип данных Описание
    DWORD_PTR Длинное целое без знака, соответствующее точности указателей.
    HALF_PTR Половина размера указателя. Используется в структурах, содержащих указатель и два поля небольшого размера.
    INT_PTR Целое со знаком, соответствующее точности указателей.
    LONG_PTR Длинное целое со знаком, соответствующее точности указателей.
    SIZE_T Максимальное количество байтов, на которые может ссылаться указатель. Используется для счетчиков, которые должны охватывать весь диапазон возможных значений указателей.
    SSIZE_T Тип SIZE_T со знаком.
    UHALF_PTR Тип HALF_PTR без знака.
    UINT_PTR Тип INT_PTR без знака.
    ULONG_PTR Тип LONG_PTR без знака.

    Пример: использование указательных типов данных

    Аргументом потока, передаваемым функции потока при вызове CreateThread и _beginthreadex (см. главу 7), является указатель типа PVOID. Иногда программист может захотеть передать функции потока только целочисленное значение, указывающее, например, номер потока или индекс данных в глобальной таблице. Тогда функцию потока, интерпретирующую параметр как целое без знака, можно было бы написать следующим образом:

    DWORD WINAPI MyThreadFunc(PVOID Index_PTR) {

     DWORD_PTR Index;

     …

     Index = (DWORD_PTR)Index_PTR;

     …

    }

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

    DWORD_PTR Ix;

    for (Ix = 0; Ix < NumThreads; Ix++) {

     hTh[Ix] = _beginthreadex(NULL, 0, MyThreadFunc, (PVOID)Ix, 0, NULL);

     …

    }

    Заметьте, что в уже существующий код вам придется внести необходимые изменения. Об этом говорится далее в разделе "Перенос существующего кода".

    Предостережение

    Пока, по крайней мере, в случае первоначальных вариантов реализации, не следует рассчитывать на получение доступа ко всему виртуальному адресному пространству. Размер виртуальных адресных пространств может ограничиваться такими, например, значениями, как 512 Гбайт, что соответствует ограничению данных 39 битами. Можно надеяться, что со временем, по мере эволюции процессоров и систем, указанный верхний предел увеличится.

    Различия между Windows и UNIX

    В Windows и UNIX выбраны различные стратегии. Большинство поставщиков UNIX-систем реализуют модель LP64, в которой размер как длинного целочисленного, так и указательного типов данных составляет 64 бита. Такую модель иногда называют моделью "I32, LP64", чтобы подчеркнуть тот факт, что размер данных типа int по-прежнему составляет 32 бита. Таким образом, различие между обеими системами в рассматриваемом нами смысле сводится к различию в размерах целых чисел типа long. К тому же, типы данных, перечисленные в таблицах 16.1 и 16.2, приняты только в Windows.

    Для каждой из двух моделей имеются разумные обоснования, и в белых страницах "Aspen", фигурирующих в списке дополнительной литературы к этой главе, приводятся аргументы, объясняющие выбор, сделанный в UNIX. И все же, было бы гораздо удобнее, если бы в обеих ОС действовали одни и те же соглашения.

    Перенос имеющегося программного кода

    Единая модель данных Windows призвана минимизировать объем возможных изменений исходного кода, но полностью избежать необходимости внесения изменений невозможно. Например, такие функции, как HeapCreate и HeapAlloc (глава 5), которые имеют дело непосредственно с распределением памяти и размерами блоков памяти, должны использовать либо 32-битовое, либо 64-битовое поле, в зависимости от модели. Точно так же, следует всегда тщательно проверять код, чтобы выяснить, не используются ли в нем скрытые допущения относительно размеров полей и указателей.

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

    Изменения, связанные с использованием API

    Наиболее заметные изменения, связанные с использованием API, затрагивают функции управления памятью, введенные в главе 5. В новых определениях в полях счетчиков используется тип данных SIZE_T (см. табл. 16.2). Например, теперь прототип функции HeapAlloc будет иметь следующий вид: 

    LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
     

    Количество запрошенных байтов, указываемое в третьем поле, выражается данными типа SIZE_T и поэтому является 32– или 64-битовым целым без знака. Ранее данные в этом поле имели тип DWORD (всегда 32 бита).

    Данные типа SIZE_T используются в соответствии с необходимостью в главе 5.

    Изменения, связанные с устранением неявных допущений относительно предполагаемых размеров элементов данных

    Источником многих проблем могут служить различного рода допущения относительно размеров данных. Несколько возможных примеров этого приводятся ниже.

    • Тип DWORD больше нельзя использовать при указании размера блоков памяти. Вместо него следует применять типы данных SIZE_T или DWORD64.

    • Необходимо тщательно проверять размеры полей, используемых взаимодействующими процессами, независимо от того, выполняются ли они на одной и той же или на разных системах. Так, в главе 12 для того, чтобы перенос программы на системы UNIX или Win64 не приводил к возникновению 64-битовых полей, поля размера в сообщениях сокетов определялись с использованием типа данных LONG32. При организации связи между процессами Windows, использующими разные модели, размеры блоков памяти не должны превышать 2 Гбайт.

    • Для вычисления размера структур или типов данных следует использовать функцию sizeof; эти размеры будут разными для Win32 и Win64, если в структуру данных входят указатели или элементы данных SIZE_T. Литеральные константы размеров должны быть исключены (разумеется, этому совету было бы неплохо следовать при любых обстоятельствах).

    • Необходимо проверять, не содержаться ли в объединениях, в которых указатели используются совместно с арифметическими типами данными, неявные предположения относительно размеров типов данных.

    • Любое приведение типов или иное преобразование, в котором участвуют указатели и данные арифметического типа должно тщательно проверяться. Обратитесь, например, к фрагментам кода, приведенным в разделе "Пример: использование указательных типов данных". 

    • В частности, остерегайтесь неявного приведения 32-битовых целых к 64-битовым в вызовах функций. Нет никакой гарантии, что старшие 32 бита будут очищены, в результате чего функция может получить в качестве аргумента очень большое 64-битовое целое значение.

    • Указатели выравниваются по 8-байтовым границам, в результате чего дополнение структур, обусловленное выравниванием, может увеличить размер структуры данных сверх необходимого и даже отрицательно повлиять на производительность. Перемещение указателей в начало структуры минимизирует последствия ее "разбухания".

    • При выводе на печать указателей вместо спецификатора формата %x используйте спецификатор %p, а при выводе платформо-масштабируемых данных, например типа SIZE_T, — спецификатор %ld.

    • Функции setjmp и longjmp должны использовать заголовочный файл <setjmp.h>, а не какие-либо допущения относительно возможного размера переменной jmp_buf, в которой должен храниться указатель.

    Пример: перенос программы sortMM (программа 5.5)

    В программе sortMM (программа 5.5) интенсивно используются указатели, и в частности, арифметика указателей. Подготовка этой программы к переносу, в результате чего ее можно будет компоновать и выполнять под управлением как Win32, так и Win64, иллюстрирует обычно используемые методики, а также демонстрирует, как легко невольно сделать допущения относительно размера указателя.

    Использование предупреждающих сообщений компилятора

    Какое бы большое значение визуальная проверка кода ни играла для обнаружения и устранении любых проблем, связанных с переходом к Win64, всегда целесообразно использовать компилятор или какое-либо иное средство, обеспечивающее просмотр кода и выдачу соответствующих предупреждающих сообщений.

    Входящий в состав Microsoft Visual Studio 7.0 (.NET) компилятор C++ компании Microsoft может конфигурироваться для выдачи таких сообщений. Для этого достаточно задать в командной строке компилятора опции –Wp64 и –W3. В Visual Studio для установки этих опций потребуется выполнить следующие действия:

    • Выберите страницу Project Properties (Свойства проекта).

    • Откройте папку C++.

    • Щелкните на кнопке General (Общие). 

    • Выберите вкладку Detect 64-bit Portability Issues (Определять элементы переноса в 64 разряда) и выберите вариант Yes (/Wp64) (Да (/Wp64)). Оставьте для уровня диагностики (warning level) значение 3.

    После этого, в процессе сборки проекта в окне вывода будут отображаться соответствующие предупреждающие сообщения. При построении в Microsoft Visual Studio 7.0 проектов, которые находятся на Web-сайте книги, вывод предупреждающих сообщений конфигурировался именно так, как описано выше.

    Код до подготовки к переносу

    Большая часть программного кода sortMM.с не приводит к выдаче предупреждающих сообщений, но один участок кода на шаге 6 (см. программу 5.5) вызывает их генерацию. Соответствующий фрагмент кода вместе с номерами строк представлен в программе 16.1. Имейте в виду, что в последующих версиях этой программы номера строк могут поменяться. 

    Программа 16.1. sortMM.с: код до подготовки к переносув Win64, часть 1

    54 LPBYTE pXFile = NULL, pX;

    55 TCHAR _based (pInFile) *pIn;

    130

    131 if (!NoPrint)

    132  for (iKey = 0; iKey < FsX / RSize; iKey++) {

    133   WriteFile(hStdOut, &ChNewLine, TSIZE, &nWrite, NULL);

    134

    135   /* Приведение типа рХ играет весьма важную роль, поскольку это

    136      указатель на байт, а нам нужны четыре байта указателя типа _based. */

    137   pIn = (TCHAR _based(pInFile)*)*(LPDWORD)pX;

    138

    139   while ((*pIn != CR || *(pIn + 1) != LF) && (DWORD)pIn < FsIn) {

    140    WriteFile(hStdOut, pIn, TSIZE, &nWrite, NULL);

    141    pIn++;

    142   }

    143   pX += RSize;

    144  }
     

    Сообщения компилятора далее приводятся, но прежде чем ознакомиться с ними, вы, возможно, захотите просмотреть код, чтобы определить возможные причины выдачи будущих предупреждающих сообщений. Не забывайте о том, что нашей целью является придание программе такого вида, который обеспечивает ее сборку и корректное выполнение как в режиме Win32, так и в режиме Win64.

    Предупреждающие сообщения компилятора

    Предупреждающие сообщения компилятора для этого фрагмента кода отчетливо демонстрируют неявное предположение о том, что размер указателя составляет 4 байта.

    SORTMM.C(137) : warning C4312: 'type cast' : conversion from 'DWORD' to 'TCHAR __based(pInFile) *' of greater size

    SORTMM.C(139) : warning C4311: 'type cast' : pointer truncation from 'TCHAR __based(pInFile) *' to 'DWORD'

    Первое предупреждение (строка 137) является существенным. Разыменование рХ после его приведения (type cast) к типу LPDWORD приводит к 32-битовому значению, которое затем назначается указателю pIn. Почти с полной уверенностью можно утверждать, что разыменование pIn вызовет исключение или приведет к возникновению иной серьезной ошибки. Правильным решением для строки 137 будет замена приведения к типу LPDWORD приведением к типу указателя LPTSTR следующим образом:

    pIn = (TCHAR _based(pInFile)*)*(DWORD_PTR)pX;

    Сообщение для строки 139 довольно интересно, поскольку мы сравниваем базовый указатель с размером файла. Если предположить, что файл не является гигантским, то на это предупреждение можно не обращать внимания. При этих условиях можно было бы проигнорировать и сообщение для строки 137. Однако мы учтем перспективу и приготовимся к работе с гигантскими файлами, пусть даже типом FsSize пока и является DWORD. Допуская полный диапазон значений указателя, мы должны преобразовать строку 139 следующим образом:

    while ((*pIn != CR || *(pIn + 1) != LF) && (SIZE_T)pIn < (SIZE_T)FsIn) {

    Второй сегмент, относящийся к шагу 2b, порождает дополнительные предупреждающие сообщения, связанным с усечением типов (pointer truncation). Соответствующий фрагмент кода представлен в программе 16.2.

    Программа 16.2. sortMM: код до подготовки к переносу в Win64, часть 2

    40  DWORD, KStart, KSize;

    174 /* Шаг 2b: Получить первый ключ; определить размер и начальный адрес ключа. */

    175

    176  KStart = (DWORD) pInScan;

    177  /* Вычисляем адрес начала поля ключа. */

    178  while (*pInScan !=''&& *pInScan != '\t') pInScan++;

    179  /* Вычисленный конец поля ключа. */

    180

    181  KSize = ((DWORD)pInScan – KStart) / TSIZE;
     

    Компилятор выводит следующие предупреждающие сообщения:

    SORTMM.C(176) : warning C4311: 'type cast' : pointer truncation from 'TCHAR __based(pInFile) *' to 'DWORD'

    SORTMM.C(181) : warning C4311: 'type cast' : pointer truncation from 'TCHAR __based(pInFile) *' to 'DWORD'

    Исправления сводятся к использованию DWORD_PTR в качестве типа данных в строке 40 и при приведении типов в строках 176 и 181.

    Дополнительные сообщения такого же характера появляются на шаге 2с в конце функции CreateIndexFile. На Web-сайте книги находится видоизмененный файл sortMM64.с, который пригоден как для Win32, так и для Win64, и использование которого позволяет избавиться от появления предупреждающих сообщений.

    Предупреждающие сообщения и необходимые изменения, касающиеся других программ

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

    В то же время, программа atouEX (программа 14.2) потребовала нескольких изменений, вызванных необходимостью использования типа данных DWORD_PTR для целочисленной переменной, хранящейся в поле hEvent структуры OVERLAPPED. Это обусловлено тем, что в Win64 размер данных типа HANDLE составляет 64 бита. Необходимые изменения отмечены в листинге программы, находящемся на Web-сайте.

    Некоторые предупреждения могут быть проигнорированы. Например, такие функции, как strlen(), возвращают значения типа size_t. Длина строки будет часто назначаться переменным типа DWORD, вызывая появление предупреждающих сообщений относительно "потери точности" ("loss of precision"). Во всех практических ситуациях на предупреждения такого рода можно не обращать внимания.

    Резюме

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

    Дополнительная литература

    Наилучшими информационными источниками являются библиотеки MSDN и информация, размещенные на Web-сайте компании Microsoft. Ниже приведены некоторые рекомендованные ссылки, почерпнутые на Web-сайте компании Microsoft и из других источников.

    • Подготовленная специалистами компании Microsoft статья "New Data Types" доступна по адресу http://msdn.microsoft.com/library/default.asp?url=/library/en-us/win64/win64/the_new_data_types.asp. Таблицы 16.1 и 16.2 взяты именно из этой статьи.

    • "Introduction to Developing Applications for the 64-bit Version of Windows" — неплохое краткое введение в различные модели программирования. Эта статья доступна по адресу http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetserv/html/ws03-64-bitwindevover.asp. Статья содержит также краткий обзор архитектуры процессоров Itanium, хотя Itanium — не единственные процессоры, на которых может выполняться Win64.

    • Описание схемы UNIX "Aspen", подводящей прочный фундамент под модель LP64, доступно по адресу http://www.opengroup.org/public/tech/aspen/lp64_wp.htm.

    • В статье "Migration Tips", доступной по адресу http://msdn.microsoft.com/library/default.asp?url=/library/en-us/win64/win64/migration_tips.asp, вы найдете хорошие советы по переносу программ с 32-разрядных на 64-разрядные платформы, а также ряд полезных ссылок. Произведя поиск в Web, вы сможете найти дополнительную информацию и рекомендации.

    Если вас интересуют общие вопросы архитектуры компьютеров, обратитесь к книге [16], являющейся стандартом в этой области. Информация, касающаяся процессоров Itanium, приводится в [44].

    Обширная информация, касающаяся архитектур, основанных на 64-разрядном расширении, представлена на Web-сайтах компаний Intel и AMD:

    http://www.intel.com и http://www.amd.com/us-en

    Упражнения

    16.1. Предположим, что p1 и р2 — указатели, связанные соотношением p1 > р2, и вы хотите получить расстояние между двумя элементами, вычитая один указатель из другого. При каких условиях будет действительным выражение: (DWORD)p1 – (DWORD)р2? Следует ли заменить это выражение на (DWORD)(p1 – р2), если расстояние между элементами невелико? Подсказка. Примите во внимание свойства обеих комплементарных арифметик. 

    16.2. Избавьтесь от выдачи компилятором предупреждающих сообщений относительно 64-битовых переменных, если таковые выводятся, в других программах, например, sortBT (программа 5.1) и ThreeStage (программа 10.5), в которых интенсивно используются указатели.

    16.3. Если у вас имеется доступ к системе Win64, протестируйте 64-разрядные программы. Убедитесь также, что компоновка программ в 32-разрядном режиме по-прежнему осуществляется корректно.







     


    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх