какой смысл использовать unsigned int в C? - PullRequest
0 голосов
/ 23 декабря 2018

Я думал, что unsigned int может хранить только целые числа> = 0. Но я попытался назначить отрицание для unsigned int, ничего особенного не произошло.Кажется, что он без проблем сохранил значение.

Так в чем же разница между int со знаком и без знака, и какой смысл, если он все равно может хранить любое значение?

Ответы [ 5 ]

0 голосов
/ 23 декабря 2018

Беззнаковые имеют 1) более высокие максимумы и 2) определенные, переполнение по кругу.

Если с бесконечной точностью

 (unxigned_c = unsigned_a + unsinged_b) >= UINT_MAX

, тогда unsigned_c будет уменьшено по модулю UINT_MAX+1:

#include <limits.h>
#include <stdio.h>
int main()
{
    printf("%u\n", UINT_MAX+1); //prints 0
    printf("%u\n", UINT_MAX+2); //prints 1
    printf("%u\n", UINT_MAX+3); //prints 2
}

Аналогичная вещь происходит с вами, сохраняя подписанные значения в неподписанные.В этом случае применяется 6.3.1.3p2 - к значению концептуально добавляется UINT_MAX+1.

С другой стороны, для типов со знаком переполнение не определено, что означает, что если выПозвольте этому случиться, ваша программа больше не является правильно сформированной, и стандарт не дает никаких гарантий относительно ее поведения.Компиляторы используют это для оптимизации , предполагая, что этого никогда не произойдет.

Например, если вы компилируете

#include <limits.h>
#include <stdio.h>

__attribute__((noinline,noclone)) //or skip the attr & define it in another tu
_Bool a_plus1_gt_b(int a, int b) { return a + 1 > b; }

int main()
{
    printf("%d\n", a_plus1_gt_b(INT_MAX,0)); //0
    printf("%d\n", INT_MAX+1); //1
}

на gcc с -O3, это оченьскорее всего печать

1
-2147483648
0 голосов
/ 23 декабря 2018

Одним важным моментом является то, что переполнение целого числа со знаком является неопределенным поведением, тогда как целые числа без знака определены для переноса.Фактически это то, что происходит, когда вы присваиваете одно отрицательное значение: оно просто оборачивается до тех пор, пока значение не окажется в диапазоне.

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

Таким образом, вы должны использовать тип без знака, когда вам нужна четко определенная семантика обтекания для избыточного и недостаточного заполнения, и / или вам нужно представить положительные целые числа, превышающие максимум соответствующего (или наибольшего подходящего)подписанный тип.Технически вы можете избежать знаковых типов в большинстве случаев, применяя отрицательные числа поверх неподписанных типов (в конце концов, вы можете просто интерпретировать определенные битовые комбинации как отрицательные числа), но ... почему, когда язык предлагаетэтот сервис "бесплатно".Единственная реальная проблема со знаком целых чисел в C - это необходимость следить за переполнением, но взамен вы можете получить лучшую оптимизацию.

0 голосов
/ 23 декабря 2018

Смысл использования unsigned int в C таков:

  • Это дает вам больший диапазон для положительных значений (не менее 32 767 для знаковых и не менее 65 535 для беззнаковых)
  • Это дает вам возможность использовать номер для маскировки и избежать неопределенного поведения при сдвиге числа
  • . Это позволяет компилятору проверить, не назначаете ли вы неправильные значения для номера (если вы знаете,он должен быть без знака), что произошло бы в вашем случае, если бы вы скомпилировали с включенными предупреждениями.
0 голосов
/ 23 декабря 2018

Вы правы, что unsigned int может хранить только целые числа> = 0. (Конечно, есть и верхний предел, и этот верхний предел зависит от вашей архитектуры и определяется как UINT_MAX в limit.h).

Присвоив значение int со знаком unsigned int, вы вызываете неявное преобразование типа.Язык C имеет несколько очень точных правил о том, как это происходит.Когда это возможно, компилятор пытается сохранить значение, когда это возможно.Возьмем для примера:

int x = 5;
unsigned int y;

y = x;

Приведенный выше код также выполняет преобразование типов, но поскольку значение «5» может быть представлено как в целочисленных диапазонах со знаком, так и без знака, значение может быть сохранено, поэтому yтакже будет иметь значение 5.

Теперь рассмотрим:

x = -5;
y = x;

В частности, в этом случае вы присваиваете значение , а не в пределах представимого диапазона unsigned int, и поэтому компилятор должен преобразовать значение во что-то в пределах диапазона.Стандарт C предписывает, что значение 1 + UINT_MAX будет добавлено к значению, пока оно не окажется в пределах диапазона unsigned int.В большинстве систем в наши дни UINT_MAX определяется как 4294967925 (2 ^ 32 - 1), поэтому значение y будет на самом деле 4294967921 (или 0xFFFFFFFB в шестнадцатеричном формате).

Важно отметитьчто на машинах с двумя дополнениями (почти повсеместно в наши дни) двоичные представления значения signed int -5 также равны 0xFFFFFFFB, но это не обязательно.Стандарт C допускает и поддерживает машины, которые используют разные целочисленные кодировки, поэтому переносимый код никогда не должен предполагать, что двоичное представление будет сохранено после неявного преобразования, такого как это.

Надеюсь, это поможет!

0 голосов
/ 23 декабря 2018

Оператор типа

unsigned int t = -1;
printf("%u", t);

полностью допустим и четко определен в C. Отрицательные значения, когда присваиваются целочисленному типу без знака, преобразуются неявно (см., Например, this стандартный черновик онлайн C):

6.3.1.3 Целые числа со знаком и без знака

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

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

4294967295

Таким образом, вы можете присваивать «отрицательные» значения целым типам без знака, но результат не является отрицательным значением в его реальном смысле.Это особенно актуально при сравнении целых значений без знака с отрицательными значениями.Рассмотрим, например, следующие два цикла:

int i = 10;
while (--i >= 0) {  // 10 iterations
    printf("i: %d\n", i);
}

unsigned int u = 10;
while (--u >= 0) {  // endless loop; warning provided.
    printf("u: %u\n", u);
}

Первый завершится после 10 итераций, а второй никогда не закончится: целые значения без знака не могут стать отрицательными, поэтому u >= 0 всегда истинно.

...