Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Как привести или преобразовать беззнаковое целое число в целое число в C?

Автор вопроса: Eric BrottoОтветов: 7

Прошу прощения, если вопрос покажется странным. Я отлаживаю свой код, и это кажется проблемой, но я не уверен.

Спасибо!

  • Реальный вопрос заключается в том, что вы хотите делать, когда/если значение в беззнаковом целом выходит за пределы диапазона, который может быть представлен подписанным целым числом. Если он находится в диапазоне, просто назначьте его, и все готово. Если он выходит за пределы диапазона, это даст неопределенный результат, поэтому вы, вероятно, захотите сначала уменьшить его до нужного диапазона или назначить ему более крупный тип со знаком. Jerry Coffin 26.02.2011
  • Не определено, определено реализацией (C99 6.3.1.3§3). В противном случае я согласен, и присваивание большего подписанного целого числа - самое простое решение. Gauthier 18.02.2014

ctypescastingintunsigned

Ответы

Ответ 1 · рейтинг 31 · Chris Cooper

Это зависит от того, каким вы хотите видеть поведение. int не может содержать многие значения, которые может содержать unsigned int.

Вы можете использовать как обычно:

int signedInt = (int) myUnsigned;

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

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

EDIT: Как упоминалось в комментариях ProdigySim, максимальное значение зависит от платформы. Но вы можете получить к нему доступ с помощью INT_MAX и UINT_MAX.

Для обычных 4-байтовых типов:

4 bytes = (4*8) bits = 32 bits

Если используются все 32 бита, как в unsigned, максимальное значение будет 2^32 - 1 или 4,294,967,295.

Знаковое int эффективно жертвует одним битом ради знака, поэтому максимальное значение будет 2^31 - 1 или 2,147,483,647. Обратите внимание, что это половина другого значения.

Ответ 2 · рейтинг 6 · Saurabh Gokhale

Целое число без знака может быть преобразовано в знаковое (или наоборот) с помощью простого выражения, как показано ниже:

unsigned int z;
int y=5;
z= (unsigned int)y;

Хотя это не относится к вопросу, вы хотели бы прочитать следующие ссылки:

Ответ 3 · рейтинг 4 · Pearly

ИМХО вопрос вечный. Как указано в различных ответах, присвоение значения без знака, которое не находится в диапазоне [0, INT_MAX], определяется реализацией и может даже вызвать сигнал. Если беззнаковое значение считается представлением числа со знаком в виде дополнения до двух, вероятно, наиболее переносимым способом является IMHO способ, показанный в следующем фрагменте кода:

#include <limits.h>
unsigned int u;
int i;

if (u <= (unsigned int)INT_MAX)
  i = (int)u; /*(1)*/
else if (u >= (unsigned int)INT_MIN)
  i = -(int)~u - 1; /*(2)*/
else
  i = INT_MIN; /*(3)*/
  • Ветвь (1) очевидна и не может вызывать переполнение или ловушки, поскольку она сохраняет значение.

  • Ветвь (2) прилагает некоторые усилия, чтобы избежать переполнения целого числа со знаком, беря дополнение значения до единицы с помощью побитового НЕ, приводит его к 'int' (которое теперь не может переполняться), отрицает значение и вычитает единицу, что также может здесь не перелить.

  • Ветвь (3) обеспечивает яд, который мы должны принять в отношении целей дополнения или знака/величины, потому что диапазон представления целого числа со знаком меньше, чем диапазон представления дополнения до двух.

Скорее всего, это сведется к простому перемещению по цели с дополнением до двух; по крайней мере, я наблюдал такое с GCC и CLANG. Также ветвь (3) недостижима для такой цели - если кто-то хочет ограничить выполнение двумя дополнительными целями, код может быть сжат до

#include <limits.h>
unsigned int u;
int i;

if (u <= (unsigned int)INT_MAX)
  i = (int)u; /*(1)*/
else
  i = -(int)~u - 1; /*(2)*/

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

И да, некоторые приведения к "unsigned int" избыточны, но

  • они могут помочь случайному читателю

  • некоторые компиляторы выдают предупреждения при сравнении со знаком/без знака, потому что неявное приведение приводит к некоторому неинтуитивному поведению из-за дизайна языка

Ответ 4 · рейтинг 3 · Jeremiah Willcock

Если у вас есть переменная unsigned int x;, вы можете преобразовать ее в int с помощью (int)x.

Ответ 5 · рейтинг 1 · DarkDust

Это так просто:

unsigned int foo;
int bar = 10;

foo = (unsigned int)bar;

Или наоборот...

Ответ 6 · рейтинг 1 · Lundin

Если в одном и том же выражении используются unsigned int и (signed) int, подписанный int неявно преобразуется в unsigned. Это довольно опасная особенность языка C, о которой вам нужно знать. Это может быть или не быть причиной вашей ошибки. Если вам нужен более подробный ответ, вам придется опубликовать код.

Ответ 7 · Jixuan Cheng

Некоторые пояснения из C++Primer 5th стр. 35

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

Например, 8-битный символ без знака может содержать значения от 0 до 255 включительно. Если мы присваиваем значение вне диапазона, компилятор присваивает остаток от этого значения по модулю 256.

unsigned char c = -1; // assuming 8-bit chars, c has value 255

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

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

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

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

Например, если операнды long и unsigned int, а int и long имеют одинаковый размер, длина будет преобразована в unsigned int. Если тип long имеет больше битов, то unsigned int будет преобразован в long.

Я нашел чтение этой книги очень полезным.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы