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

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

Спасибо!


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


Ответы (7)


Это зависит от того, каким вы хотите видеть поведение. 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. Обратите внимание, что это половина другого значения.

person Chris Cooper    schedule 26.02.2011
comment
Знаете ли вы, каково максимальное значение для беззнакового? а для инт? - person Eric Brotto; 26.02.2011
comment
@Eric Brotto: это будет зависеть от системы, для которой вы компилируете. Обычно есть макросы для INT_MAX, UINT_MAX, если вам нужно проверить. В большинстве 32-битных систем INT_MAX будет (2^31)-1, а UINT_MAX будет (2^32)-1. Обратите внимание, что значение UINT_MAX, приведенное к типу int, будет равно -1. - person ProdigySim; 26.02.2011
comment
Обычно в том смысле, что они требуются стандарту C (для размещенной системы). - person Jim Balter; 27.02.2011
comment
На самом деле эти макросы (находящиеся в <limits.h>) требуются даже в автономной (т. е. не размещенной) системе. - person DevSolar; 11.07.2021

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

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

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

person Saurabh Gokhale    schedule 26.02.2011
comment
Я думаю, вы хотели сказать z = (unsigned int)y; в выражении выше. Поскольку y уже является int, приведение его к int бессмысленно... - person A. Levy; 01.06.2011

ИМХО вопрос вечный. Как указано в различных ответах, присвоение значения без знака, которое не находится в диапазоне [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» избыточны, но

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

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

person Pearly    schedule 15.11.2020
comment
Вы уверены, что (unsigned int)INT_MIN правильно определен? - person Dan Bechard; 17.03.2021
comment
да. По крайней мере, до тех пор, пока заголовки компилятора определяют INT_MIN так, как должны, то есть целое число со знаком с максимально отрицательным возможным значением. Преобразование отрицательного целого числа со знаком в целое число без знака четко определено и всегда приводит к битовому шаблону дополнения до двух. - person Pearly; 20.03.2021

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

person Jeremiah Willcock    schedule 26.02.2011

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

unsigned int foo;
int bar = 10;

foo = (unsigned int)bar;

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

person DarkDust    schedule 26.02.2011
comment
Актерский состав избыточен и уродлив. Присваивание по своей сути включает преобразование в C для типов, где присваивание имеет смысл (и даже для некоторых, где оно не имеет смысла). Приведения обычно служат только для того, чтобы скрыть неверный код. - person R.. GitHub STOP HELPING ICE; 27.02.2011
comment
Это неправда, на самом деле это довольно распространенная проблема, если вы проверяете, прошло ли определенное количество времени. Ваш интервал должен быть целым числом без знака, так как нет смысла подписывать его, но time_t всегда имеет знак, поэтому он может представлять даты старше 1970 года. Если вы не выполняете приведение при выполнении if ((now - then ) › интервал) компилятор выдаст предупреждения. - person Arran Cudbard-Bell; 26.05.2014

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

person Lundin    schedule 26.02.2011

Некоторые пояснения из 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.

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

person Jixuan Cheng    schedule 08.04.2019
comment
Если мы присвоим значение вне допустимого диапазона объекту подписанного типа, результат будет неопределенным. - это неправильно, это на самом деле определяется реализацией - person M.M; 06.11.2019
comment
Кроме того, это вопрос C, но вы процитировали ссылку на C++. C и C++ это разные языки - person M.M; 06.11.2019