Это код, который у меня есть:

HWND WebformCreate(HWND hParent, UINT id)
{
    return CreateWindowEx(0, WEBFORM_CLASS, _T("about:blank"),
        WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 0, 0, 100, 100, hParent,
        (HMENU)id, GetModuleHandle(NULL), 0);
}

Это предупреждение, которое я получаю:

warning C4312: 'type cast' : conversion from 'UINT' to 'HMENU' of greater size

Вот вопросы, которые у меня есть:

  1. Почему компилятор считает, что приводить к большему типу - плохая идея?
  2. Как лучше всего избавиться от предупреждения? (Я не хочу отключать его.)
  3. Выполнение двойного приведения типа следующим образом: (HMENU)(UINT_PTR)id избавляется от предупреждения. Почему как?
  4. Отключение функции «Обнаружение проблем с переносимостью 64-разрядной версии» (Wp64) также избавляет от предупреждения. Почему Wp64 устарел? Я должен его включить?
13
Tobbe 26 Авг 2010 в 00:14
Это предупреждение обнаруживает попытку присвоить 32-разрядное значение 64-разрядному типу указателя, например приведение 32-разрядного типа int или long к 64-разрядному указателю. Это может быть небезопасным преобразованием даже для значений указателя, которые помещаются в 32 бита, когда происходит расширение знака. Если отрицательное 32-битное целое число присваивается 64-битному типу указателя, расширение знака приводит к тому, что значение указателя ссылается на адрес памяти, отличный от значения целого числа.
 – 
Zoli
13 Окт 2022 в 22:07

3 ответа

Лучший ответ

Вы преобразуете 32-битный UINT в 64-битный указатель. Это самоубийство - вы пытаетесь указать на что-то, но забыли половину его местоположения! Вы ОБЯЗАТЕЛЬНО ДОЛЖНЫ взять UINT_PTR. Когда вы приводите указатель к int, поведение нормально, только если int имеет тот же размер, что и указатель. В противном случае это конец среды выполнения вашего приложения из-за нарушения прав доступа.

Изменить:
Почему компилятор считает, что приведение к большему типу - плохая идея?
R.E. выше

Как лучше всего избавиться от предупреждения? (Я не хочу отключать его.)
Решить проблему. Этот код почти наверняка будет сброшен.

Выполнение двойного приведения типа, например: (HMENU) (UINT_PTR) id избавляется от предупреждения. Почему / как?
Это происходит потому, что приведение UINT к UINT_PTR совершенно корректно - UINT_PTR - это просто интегральный тип, без потери данных.

Отключение функции «Обнаружение проблем с переносимостью 64-разрядной версии» (Wp64) также избавляет от предупреждения. Почему Wp64 устарел? Надеть его?
Он устарел, потому что на самом деле я не могу вспомнить, почему. Я думаю, что это слишком легко предупреждает. Но что касается базового «Не приводить целые типы и указатели», вам определенно следует оставить его включенным.

4
Puppy 26 Авг 2010 в 00:18
Я не понимаю. Не могли бы вы расширить этот ответ? Я здесь вообще не имею дело с указателями. Это просто контрольный идентификатор.
 – 
Tobbe
26 Авг 2010 в 00:21
HMENU - это дескриптор меню, который является указателем. typedef HMENU__ * HMENU - это определение HMENU.
 – 
Puppy
26 Авг 2010 в 00:22
5
Код и так работает нормально. Никаких сбоев. Скопируйте / вставьте из msdn: «Для дочернего окна hMenu указывает идентификатор дочернего окна, целочисленное значение, используемое элементом управления диалогового окна для уведомления своего родителя о событиях».
 – 
Tobbe
26 Авг 2010 в 00:26
Там также четко написано: «Дескриптор меню». Тот факт, что ваш код работает, показывает только то, что вы используете его только как часть его функциональности. Если бы мне пришлось сделать меню и передать его, ваш код вылетит. Вы просите об ошибках. Возьмите UINT_PTR, добавлено четыре символа. Visual Studio правильно предупреждает.
 – 
Puppy
26 Авг 2010 в 00:28
8
@DeadMG: Неправда. Код не выйдет из строя, потому что приведение в коде совершенно безопасно и правильно с точки зрения WinAPI. Когда вы создаете окно в стиле WS_CHILD, вы должны передать идентификатор дочернего окна, приведенного к типу HMENU. Вот как это работает в WinAPI. Это не «ручка меню». Это было бы «дескриптором меню», если бы окна не были WS_CHILD.
 – 
AnT stands with Russia
26 Авг 2010 в 01:45

Тип, скрывающийся за именем HMENU, на самом деле является типом указателя . Компилятор сообщает вам, что преобразование меньшего целочисленного типа в более крупный тип указателя не имеет смысла, поскольку результирующее значение указателя будет "неполным", то есть битами более высокого порядка. если значение указателя будет заполнено нулями. Последнее имеет очень мало смысла с типами указателей.

В вашем конкретном случае это безопасно, поскольку это значение HMENU на самом деле не должно быть указателем, указывающим куда-либо. Однако компилятор этого не знает, поэтому выдает предупреждение. Используйте более крупный целочисленный тип в качестве промежуточного типа в приведении, и предупреждение исчезнет (вы сами это предложили), поскольку в этом случае вы выполняете два приведения: меньшее целое число к большему целому и затем большее целое число на указатель. Меньшее целое число к большему - это арифметическое преобразование, для которого имеет смысл заполнить старшие биты нулями (представленное значение не меняется), поэтому для него не будет предупреждений. .

19
AnT stands with Russia 26 Авг 2010 в 00:21
2
Спасибо за отличное объяснение того, почему работает двойной заброс!
 – 
Tobbe
26 Авг 2010 в 00:30

Почему компилятор считает, что приводить к большему типу - плохая идея?

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

Как лучше всего избавиться от предупреждения? (Я не хочу отключать его.)

Я бы сказал, используйте HMENU везде и объявите свою функцию как

HWND WebformCreate(HWND hParent, HMENU id)

Выполнение двойного приведения типа, например: (HMENU) (UINT_PTR) id избавляется от предупреждения. Почему как?

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

HMENU - это тип указателя. Это не означает, что значение hMenu на самом деле является указателем, но это уловка, которая запрещает вам неявно смешивать, например. HMENU и HWND (потому что HMENU и HWND похожи на struct _hMENU* и struct _hWND* соответственно, которые несовместимы, тогда как UINT s будет).

2
jpalecek 26 Авг 2010 в 00:27