«[Предупреждение] эта десятичная константа без знака только в ISO C90» требуется решение этой проблемы

#c #literals #signed #integer-overflow

#c #литералы #подписано #целое число-переполнение

Вопрос:

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

int main(){
    long long a=2147488648, b;
    scanf("%lld", amp;a);
    b=a*2;
    scanf("%lld", amp;b);
    printf("%lld", amp;b);
}
 

Я сделал вместо «long long» — «int» и вместо «%lld» — «%d», но эта проблема не исчезает

Комментарии:

1. Пожалуйста, не путайте C с C #, это очень разные языки

2. printf("%lld", b);

3. @Cid, хотя это не имеет никакого отношения к предупреждению компилятора.

4. @Shawn Да, это решит проблему, но почему возникает предупреждение, важно ли это и что изменилось между C90 и следующей версией?

5. OP: Какой компилятор и опции к нему вы используете?

Ответ №1:

Основная проблема заключается в том, что 2 147 488 648 равно 2 ^ 31 5000. Битовый шаблон, использующий 32 бита, является 1000 0000 0000 0000 0001 0011 1000 1000 (среди прочего, установлен 32-й бит). В 32-битном представлении целых чисел с двумя дополнениями это, возможно, удивительно, большое отрицательное число, -2 147 478 648,1.

Это явно не входит в намерения программиста. Поэтому стандарты требуют, чтобы литерал, превышающий диапазон значений int , имел тип long int or long long int , в зависимости от того, что подходит первым, даже если литерал не l ll имеет суффикса or! 2 Если число слишком велико для обоих, поведение не определено, хотя реализации могут свободно предоставлять и использовать целочисленные типы большего размера. В частности, в соответствии с современными стандартами тип десятичного литерала никогда не является беззнаковым, если не имеет суффикса с u .

Хотя у меня нет стандартной копии C90, а старые черновики кажутся недоступными, cppreference утверждает, что действительно правила изменились в C99, и это, вероятно, то, о чем предупреждает компилятор. Примечательно, что C99 ввел больший целочисленный тип, long long int , и обязал, чтобы он мог, по крайней мере, представлять число 9223372036854775807. Правила для литеральных типов в C90, по-видимому, заключались в том, что литерал, подобный 2147488648, значение которого находится вне диапазона со знаком long int , но внутри unsigned long int диапазона be unsigned long int , чтобы максимально следовать намерениям программиста. Причина, по которой это правило было отменено, заключается в том, что значения без знака могут иметь непреднамеренные последствия в выражениях, включающих целые числа со знаком: все значения сначала преобразуются в беззнаковые. Это нормально для 2147488648u 1 , но не для if(2147488648u > -1) (-1 преобразуется в unsigned int 4294967295). Компиляторы обычно предупреждают о выражениях со смешанным знаком (хотя gcc нуждается в Wextra!). Демонстрация:

 $ cat unsigned-mismatch.c amp;amp; gcc -Wall -Wextra -pedantic -o unsigned-mismatch unsigned-mismatch.c amp;amp; ./unsigned-mismatch

#include<stdio.h>
int main(void)
{
        if(-1 < 2147488648u) { printf("-1 < 2147488648u is truen");  }
        else                 { printf("Oops: -1 < 2147488648u is false?n"); }
}

unsigned-mismatch.c: In function 'main':
unsigned-mismatch.c:5:8: warning: comparison of integer expressions of different signedness: 'int' and 'unsigned int' [-Wsign-compare]
    5 |  if(-1 < 2147488648u) { printf("-1 < 2147488648u is truen");  }
      |        ^
Oops: -1 < 2147488648u is false?

 

В вашем случае вам не нужно беспокоиться о предупреждении компилятора. Во-первых, вы никогда не используете присвоенное значение, поэтому все, что с ним делает компилятор, в любом случае не имеет значения; во-вторых, преобразование целочисленного значения без знака в целочисленный тип со знаком, который может содержать значение, четко определено и работает, как ожидалось. Хорошей практикой является устранение всех предупреждений (и подавление неизбежных с помощью опции pragma или компилятора в качестве указания на то, что вы знали об этом и рассматривали его). В вашем случае вы бы просто добавили к литералу суффикс с ll , чтобы сделать его подписанным long long int , который легко содержит значение. a long long int также является a , поэтому он тоже может содержать значение, так что все в порядке.


1 Проблема заключается в том, что в 32-разрядном целом числе со знаком, использующем представление дополнения two, этот бит, бит с наибольшим значением, используется для указания знака значения. Если этот бит установлен, число отрицательное. В дополнении two -1 — это «все установленные биты», и мы вычитаем 1, как обычно, оттуда, пока не достигнем INT_MIN, где все биты исчезли, кроме знакового бита.

2 В C это может снова вызвать удивление, когда разрешение перегрузки неожиданно выбирает функцию, принимающую long long аргумент, даже если буквальный аргумент имеет внешний вид простого int , без какого-либо суффикса.

Комментарии:

1. 2147488648 не равен 2 ^ 31. Это 2 ^ 31 5000.

2. @EricPostpischil Ох. Верно.