Обнаружение арифметики указателя из-за LARGEADDRESSAWARE

Я хочу переключить свое приложение на LARGEADDRESSAWARE. Одна из проблем, на которую следует обратить внимание, - это арифметика указателей, поскольку разница указателей больше не может быть представлена ​​как 32b со знаком.

Есть ли способ, как автоматически найти все экземпляры вычитания указателя в большом проекте C ++?

Если нет, есть ли какой-нибудь ручной или полуавтоматический метод с наименьшими усилиями, как этого добиться?


person Suma    schedule 16.06.2010    source источник
comment
Вы имеете в виду, что создаете приложение для системы с 64-битным адресным пространством, но компилятор использует только 32-битные указатели?   -  person Martin York    schedule 16.06.2010
comment
LARGEADDRESSAWARE - это флаг Windows32 в заголовке исполняемого файла, который сообщает ОС, что исполняемый файл может обрабатывать 32-битные адреса. Без него вы получите 31-битный адрес (т.е. верхний бит всегда равен 0), а затем вторая половина адресного пространства зарезервирована для ОС.   -  person MSalters    schedule 16.06.2010


Ответы (5)


PC-Lint может решить эту проблему.

Посмотрите на http://gimpel-online.com/MsgRef.html, код ошибки 947:

Оператор вычитания, примененный к указателям. Было найдено выражение вида p - q, где и p, и q являются указателями. Это особенно важно в случаях, когда максимальный указатель может выходить за пределы типа, который содержит различия указателей. Например, предположим, что максимальный размер указателя составляет 3 гигабайта -1, и что различия указателя представлены длинной длиной, где максимальная длина составляет 2 гигабайта -1. Обратите внимание, что обе эти величины помещаются в 32-битное слово. Тогда вычитание маленького указателя из очень большого даст очевидное отрицательное значение в long, представляющее разницу указателя. И наоборот, вычитание очень большого указателя из маленького указателя может дать положительное количество.

person Patrick    schedule 16.06.2010

Скомпилируйте код с помощью 64-битного компилятора и включенного Wp64.

Поскольку указатели имеют ширину 64 бита, но int, long, DWORD и т. Д. Остаются шириной 32 бита, вы получаете предупреждения о сокращении ptrdiff_t на int32_t

person Christopher    schedule 16.06.2010
comment
Этот ответ не актуален, потому что он обнаруживает проблемы, которые могут возникнуть, если вы используете неправильный тип для хранения указателей, но не предупреждает вас, если вы делаете опасные вычитания / сравнения указателей. - person Matteo Italia; 16.06.2010
comment
Хорошая идея. Он не найдет случаев, когда вычитание указателя назначается ptrdiff_t, тем не менее, эти случаи также являются проблемой, поскольку ptrdiff_t (int в Win32) недостаточно для представления разницы в указателях, но их можно найти с помощью текстового поиска ptrdiff_t. Настоящая проблема заключается в том, что в коде слишком много таких предупреждений, решение каждого из них кажется довольно трудоемким, и подавляющее большинство из них вообще не связано с LARGEADDRESSAWARE (мы не собираемся переносить код на Win64). - person Suma; 16.06.2010
comment
Вы утверждаете, что существует режим компиляции, в котором ptrdiff_t недостаточно велик, чтобы вместить разницу между двумя указателями? Это звучит ... ну, просто сломано, учитывая, что это стандартное определение этого типа. (Я ожидал, что он будет разного размера в разных режимах компиляции.) - person Donal Fellows; 23.06.2010

Это проблема, только если у вас есть 2 указателя, расстояние между которыми превышает 2000 миллионов байтов (2 ГБ). Это означает, что вы:

  • либо имеют очень большие массивы (> 2 ГБ)
  • или вы вычитаете указатели, которые указывают на совершенно разные структуры

Так что ищите эти особые случаи.

Я думаю, что в большинстве случаев это не проблема.

person Patrick    schedule 16.06.2010
comment
или вы вычитаете указатели, указывающие на совершенно разные структуры - это может случиться легко. Иногда мы используем указатели на объекты в качестве вторичных ключей при их сортировке, чтобы обеспечить стабильность порядка сортировки. Что касается этих особых случаев - как я могу их искать, кроме как просматривать каждый фрагмент кода? - person Suma; 16.06.2010
comment
Сравнение указателей - не проблема, их вычитание, а для сортировки должно быть достаточно сравнения указателей. Кроме того, не рекомендуется использовать указатели в качестве окончательного сравнения. Раньше я делал это и раньше, но это могло вызвать проблемы (проблемы могут стать трудновоспроизводимыми, поскольку указатели могут быть разными при каждом запуске вашего приложения; поверьте мне, у меня сама была эта проблема несколько раз). Попробуйте найти другую характеристику (например, номер строки, ключ базы данных, ...), чтобы получить стабильную сортировку. - person Patrick; 16.06.2010
comment
›Сравнение указателей - не проблема, их вычитание. К сожалению, при реализации функции сравнения для qsort возвращаемая разница кажется вполне естественной, и я уже видел это несколько раз. Хороший комментарий, к сожалению, до сих пор нет ответа на главные вопросы - как найти кейсы? Кодовая база огромна, невозможно найти все кейсы, просто запомнив их. Необходим некоторый систематический подход, автоматический или ручной (систематическая проверка кода). Решением может быть время компиляции, специальный инструмент или среда выполнения с некоторыми инструментами. - person Suma; 16.06.2010
comment
Справедливо: возможно, вы закончите с a<b<c<d, но d-a < 0 - person MSalters; 16.06.2010
comment
Обратите внимание, что если вы вычесть два указателя, вы получите «смещение» в количестве элементов между указателями. Это отлично работает для указателей, указывающих на элементы в массиве, но дает странные результаты, если два указателя не связаны. Результат не может быть целым. Предположим, ваши указатели указывают на класс размером 1000 байт. Если ptr1 указывает на 50000, а ptr2 указывает на 60100, тогда вычитание, вероятно, вернет 10, но это неверно. См. securecoding.cert.org/confluence/display/seccode/. - person Patrick; 16.06.2010
comment
Вы можете в значительной степени поразить первый пункт. OP хочет включить LARGEADDRESSAWARE, что означает, что он еще не включен ... Это означает, что адресное пространство процесса ограничено 2 ГБ. - person jalf; 23.06.2010
comment
Имейте в виду, что сравнение указателей может стать серьезной проблемой, по крайней мере, в том, что касается Стандарта. Сравнение (p ‹q) для указателей p и q не указано для общего случая (§5.9 / 2), поэтому нужно использовать std :: less. - person boycy; 19.06.2014

Поскольку наш код уже компилируется с GCC, я думаю, что, возможно, самым быстрым способом может быть:

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

Вот краткое описание изменений, которые необходимо внести в GCC для этого:

Добавьте свои предупреждения в:

  • c-typeck.c (pointer_diff функция)
  • cp / typeck.c (pointer_diff функция).

Помимо прямого определения вычитания указателя, еще одна вещь, которую нужно сделать, может заключаться в обнаружении случаев, когда вы сначала конвертируете указатели в целые типы, а затем вычитаете их. Это может быть сложнее в зависимости от того, как структурирован ваш код, в этом случае поиск по регулярному выражению для (. intptr_t). -. * - (. * Intptr_t) работал достаточно хорошо.

person Suma    schedule 16.06.2010

Как бы то ни было, я прошел через Документы с предупреждениями компилятора Microsoft для VS2017 и проверил все предупреждения, относящиеся к" signed "," trunc "(усечение) и" conv "(преобразование), которые были выше, чем уровень предупреждения 1. Затем я явно включил эти предупреждения для всех проектов в нашем решении через propsheet. Чтобы включить определенные предупреждения, перейдите в «C / C ++ / Командная строка / Дополнительные параметры» и добавьте их в формате / wL ####, где L - уровень предупреждения, которому вы хотите назначить их, а #### - это номер предупреждения.

Итак, я пришел к следующему списку:

/w14365 /w14018 /w14146 /w14245 /w14092 /w14287 /w14308 /w14388 /w14389 /w14757 /w14807 /w14302 /w14305 /w14306 /w14307 /w14308 /w14309 /w14310 /w14311 /w14312 /w14051 /w14055 /w14152 /w14239 /w14223 /w14242 /w14243 /w14244 /w14254 /w14267 /w14333 /w14334 /w14367 /w14686 /w14826

Обратите внимание, что я использовал / w1, потому что наш глобальный уровень предупреждений уже снизился до 1 (не судите меня, это устаревшее). Таким образом, некоторые из этих предупреждений уже включены, если вы установили уровень предупреждения по умолчанию 3 или выше.

Это привело к появлению более 88000 предупреждений, большинство из которых касалось использования int вместо size_t в коде с использованием STL и преобразований, касающихся типов Windows API, таких как дескрипторы, WPARAM, UINT_PTR и т. Д. Я нашел только несколько предупреждений, связанных с фактической арифметикой указателей в сторонней библиотеке, но в контексте они выглядели нормально.

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

Также используйте инструменты, описанные в этом ответе: https://stackoverflow.com/a/22745579/9635694

Другой вариант - запустить встроенный анализ кода в соответствии с CppCoreGuideLines. Перейдите в «Главное меню / Анализировать / Настроить анализ кода / Для решения» и выберите «C ++ Core Check Raw Pointer Rules» для всех проектов, которые вы хотите проанализировать. Затем «Главное меню / Анализировать / Выполнить анализ кода / Для решения». Осторожно: изменяет ваши проекты, требует много времени для сборки и может генерировать множество предупреждений. Возможно, вы захотите сконцентрироваться на C26481 «Не использовать арифметику указателей» и, возможно, на C26485 «Нет распада между массивами и указателями».

person Simpleton    schedule 06.03.2019