Почему компиляторы C не предупреждают при назначении целочисленного значения слишком высокого для подписанного типа? - PullRequest
0 голосов
/ 03 июля 2018

(при условии 64-битной машины)

, например

int n = 0xFFFFFFFF; //max 32bit unsigned number
printf("%u\n", n);

Максимальное положительное число, которое может хранить обычное целое число со знаком (32 бита): 0x7FFFFFFF.

В приведенном выше примере я присваиваю максимальное целое число без знака обычному целому числу со знаком, я не получаю предупреждений или ошибок от GCC, и результат печатается без проблем (с -Wall -Wextra).

Добавление U или L к шестнадцатеричной константе ничего не меняет.

Почему это?

Ответы [ 3 ]

0 голосов
/ 04 июля 2018

Стандарт C не определяет поведение, но требует, чтобы реализация определяла его. GCC всегда использует представление дополнения 2 и преобразует с помощью усечения , поэтому int32_t i = 0xFFFFFFFF; приведет к установке i в -1 при компиляции с GCC. На других компиляторах YMMV.


Чтобы получить предупреждение от GCC, необходимо указать флаг -Wsign-conversion :

% gcc 0xfffffff.c -c -Wsign-conversion                         
0xfffffff.c:1:9: warning: conversion of unsigned constant value to negative integer
        [-Wsign-conversion]
 int i = 0xFFFFFFFF;
         ^~t ~~~~~~~~

Обычно компиляторы C по умолчанию выдают предупреждения только об очень вопиющих ошибках и нарушениях ограничений. -Wsign-conversion сделает многие компиляции очень шумными - даже те, которые четко определены, например:

unsigned char c = '\x80';

, который производит

unsignedchar.c:1:19: warning: negative integer implicitly converted to unsigned type
         [-Wsign-conversion]
 unsigned char c = '\x80';
                   ^~~~~~

в реализациях, где char подписано.

0 голосов
/ 04 июля 2018

Предположим, что int и unsigned int являются 32-битными, что имеет место на большинстве платформ, которые вы, вероятно, будете использовать (как 32-битные, так и 64-битные системы). Тогда константа 0xFFFFFFFF имеет тип unsigned int и имеет значение 4294967295.

Это:

int n = 0xFFFFFFFF;

неявно преобразует это значение из unsigned int в int. Результат преобразования определяется реализацией; нет неопределенного поведения. (В принципе, это также может вызывать сигнал, определяемый реализацией, но я не знаю ни одной реализации, которая бы это делала).

Скорее всего, значение, хранящееся в n, будет -1.

printf("%u\n", n);

Здесь вы используете спецификатор формата %u, для которого требуется аргумент типа unsigned int, но вы передаете ему аргумент типа int. Стандарт гласит, что значения соответствующего типа со знаком и без знака являются взаимозаменяемыми в качестве аргументов функции, но только для значений, которые находятся в диапазоне обоих типов, что здесь не так.

Этот вызов не выполняет преобразование из int в unsigned int. Скорее, значение int передается в printf, что предполагает , что полученное значение имеет тип unsigned int. Поведение не определено. (Опять же, это было бы разумно предупредить.)

Наиболее вероятный результат состоит в том, что значение int -1, которое (при условии дополнения 2-х) имеет то же представление, что и 0xFFFFFFFF, будет обрабатываться так, как если бы оно было unsigned int значением 0xFFFFFFFF, который печатается в десятичном виде как 4294967295.

Вы можете получить предупреждение о int n = 0xFFFFFFFF;, используя опцию -Wconversion или -Wsign-conversion. Эти опции не включены в -Wextra или -Wall. (Вы должны спросить сопровождающих gcc, почему.)

Я не знаю опции, которая вызовет предупреждение при вызове printf.

(Конечно, исправление состоит в том, чтобы определить n как unsigned int, что делает все правильно и согласованно.)

0 голосов
/ 03 июля 2018

0xFFFFFFFF на платформе, где максимальное значение unsigned равно 2 32 -1, будет иметь тип unsigned в соответствии с "6.4.4.1 Целочисленные константы" стандарта.

И тогда мы получим преобразование:

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

1 Когда значение с целочисленным типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.
2 В противном случае, если новый тип является беззнаковым, значение преобразуется путем многократного добавления или вычитания значения, превышающего максимальное значение, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне нового типа. 60)
3 В противном случае новый тип подписывается и значение не может быть представлено в нем; либо результат определяется реализацией, либо определяется определяемый реализацией сигнал.

Итак, результат определяется реализацией или выдает сигнал, определяемый реализацией.

Теперь вы печатаете int в формате %u, который просто не соответствует. И хотя это, строго говоря, UB, вы, скорее всего, получите исходную константу, при условии, что у вас есть дополнение 2s и исходное назначение, использованное перенос.

...