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

Превосходные методы C/С++ с использованием подписанных/неподписанных функций и вызовов функций

Я задаю этот вопрос для двух разных языков: C и С++.

Какова наилучшая практика при вызове функций, у которых есть противоположное целочисленное знаковое знакомство с тем, что нам нужно в нашем коде?

Например:

uint32       _depth;                        // uint32 = DWORD
int          depth;

_BitScanForward(&_depth, (uint32)input);    // DWORD, DWORD
depth = (int)_depth;

_BitScanForward ожидает параметры DWORD (uint32). Переменная input имеет тип int16, и мне нужно обработать результат _depth как int32 в моем коде.

  • Нужно ли мне заботиться о литье input, как показано? Я знаю, что complier, вероятно, сделает это для меня, но что такое лучшая практика?
  • Допустимо ли объявлять _depth как int32 и, следовательно, не нужно делать его потом, как показано?

Примечание:

Мой комментарий о complier основан на опыте. Я написал код, который скомпилирован без предупреждений в VS, но разбился на выполнение. Оказалось, что я вызываю функцию с шириной int int. Поэтому я больше не оставляю эту тему в компиляторе.

EDIT:

Ответы полезны, спасибо. Позвольте мне уточнить мой вопрос. Если проблем с шириной нет, т.е. Функция не ожидает более узкого int, чем то, что передается (obvioulsy будет терпеть неудачу), тогда можно ли полагаться на компилятор для обработки различий знака и ширины?

4b9b3361

Ответ 1

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

Системные typedefs, такие как uint16_t, uint32_t, WORD и DWORD могут быть более узкими, более широкими или равными размерам int; в С++ вы можете использовать шаблоны, чтобы понять это, но в C вы не можете. Поэтому вы можете захотеть написать явные приведения для любого преобразования, включающего эти.

Ответ 2

Я бы настоятельно рекомендовал скрыть эту функцию в пользовательскую функцию-обертку, которая согласуется с вашим предпочтительным API (и внутри этой функции делать правильное явное литье). В случае использования специфичных для компилятора функций это имеет дополнительное преимущество в том, что будет намного проще переносить его на разные компиляторы (если вы когда-нибудь захотите это сделать), просто переустановив эту функцию-обертку.

Ответ 3

Ну, это зависит от вашего использования и т.д.:

Если я могу использовать необходимый тип, я просто использую этот тип.

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

Там у меня есть два разных подхода:

Если я на 100% уверен, что я никогда не переполняю границы между подписанным /unsigned int, я использую static_cast. (обычно для преобразования различных API-интерфейсов. Например, size() возвращает int vs size_t).

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

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

Ответ 4

Сначала ваш компилятор сделает трансляции неявными и предоставит вам предупреждение на любом значащем уровне предупреждения.

Оба действия, которые вы выполняете, - это броски, в которых компилятор (или ваши коллеги) не может легко решить, правильны ли они, поэтому явное преобразование или явное преобразование с помощью пограничного теста - лучшая практика. Вы выбираете, зависит от ваших данных. Самый безопасный способ - проверить граничные условия. Самый дешевый способ - просто бросить (в С++ используйте static_cast, а не C-стиль).