Предупреждение-сравнение между знаковыми и беззнаковыми целочисленными выражениями


В настоящее время я работаю над ускоренным C++ и столкнулся с проблемой в упражнении 2-3.

Краткий обзор программы - программа в основном берет имя, а затем отображает приветствие в рамке звездочек-то есть Привет ! окруженный обрамленными * ' s.

Упражнение - в примере программы авторы используют const int для определения отступов (пробелов) между приветствием и звездочками. Затем они спрашивают читателя:, в рамках упражнения попросите пользователя ввести информацию о том, насколько большой должна быть прокладка.

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

Exercise2 в-3.cpp: 46: предупреждение: сравнение между знаковыми и беззнаковыми целочисленными выражениями

После некоторых исследований кажется, что потому что код пытается сравнить одно из приведенных выше целых чисел (int) с string::size_type, что нормально. Но мне было интересно-означает ли это, что я должен изменить одно из целых чисел на unsigned int? Важно ли явно указать, являются ли мои целые числа знаковыми или беззнаковыми?

 cout << "Please enter the size of the frame between top and bottom you would like ";
 int padtopbottom;
 cin >> padtopbottom;

 cout << "Please enter size of the frame from each side you would like: ";
 unsigned int padsides; 
 cin >> padsides;

 string::size_type c = 0; // definition of c in the program
 if (r == padtopbottom + 1 && c == padsides + 1) { // where the error occurs

Выше приведены соответствующие биты кода, c имеет тип string::size_type, потому что мы не знаем, как долго приветствие может быть - но почему я получаю эту проблему сейчас, когда код автора не получил проблему при использовании const int? Кроме того - для всех, кто, возможно, завершил ускоренный C++ - будет ли это объяснено позже в книге?

Я на Linux Mint использую g++ через Geany, если это помогает или имеет значение (как я читал, что это может быть при определении того, что такое string::size_type).

5 53

5 ответов:

Обычно рекомендуется объявлять переменные как unsigned или size_t, если они будут сравниваться с размерами, чтобы избежать этой проблемы. Когда это возможно, используйте точный тип, с которым вы будете сравнивать (например, используйте std::string::size_type при сравнении с длиной std::string).

Компиляторы предупреждают о необходимости сравнения подписанных и неподписанных типов, поскольку диапазоны подписанных и неподписанных int различны, и когда они сравниваются друг с другом, результаты могут быть удивительными. Если вы должны сделать такое для сравнения, вы должны явно преобразовать одно из значений в тип, совместимый с другим, возможно, после проверки, чтобы убедиться, что преобразование является допустимым. Например:
unsigned u = GetSomeUnsignedValue();
int i = GetSomeSignedValue();

if (i >= 0)
{
    // i is nonnegative, so it is safe to cast to unsigned value
    if ((unsigned)i >= u)
        iIsGreaterThanOrEqualToU();
    else
        iIsLessThanU();
}
else
{
    iIsNegative();
}

У меня была точно такая же проблема вчера, работая над проблемой 2-3 в ускоренном C++. Ключ состоит в том, чтобы изменить все переменные, которые вы будете сравнивать (используя булевы операторы) с совместимыми типами. В данном случае это означает string::size_type (или unsigned int, но поскольку в этом примере используется первый, я просто буду придерживаться этого, даже если эти два технически совместимы).

Обратите внимание, что в своем исходном коде они сделали именно это для счетчика c (страница 30 в разделе 2.5 книги), Как вы правильно указал.

Что делает этот пример более сложным, так это то, что различные переменные заполнения (paddides и paddopbottom), а также все счетчики, должнытакже быть изменены на string::size_type.

Переходя к вашему примеру, код, который вы разместили, будет выглядеть следующим образом:

cout << "Please enter the size of the frame between top and bottom";
string::size_type padtopbottom;
cin >> padtopbottom;

cout << "Please enter size of the frame from each side you would like: ";
string::size_type padsides; 
cin >> padsides;

string::size_type c = 0; // definition of c in the program

if (r == padtopbottom + 1 && c == padsides + 1) { // where the error no longer occurs
Обратите внимание, что в предыдущем условии вы получите ошибку, если не инициализируете переменную r как string::size_type в цикле for. Поэтому вам нужно инициализировать цикл for с помощью что-то вроде:
    for (string::size_type r=0; r!=rows; ++r)   //If r and rows are string::size_type, no error!
Таким образом, в принципе, как только вы вводите переменную string::size_type в смесь, каждый раз, когда вы хотите выполнить булеву операцию над этим элементом, все операнды должны иметь совместимый тип для компиляции без предупреждений.

Важное различие между знаковыми и беззнаковыми интами это интерпретация последнего бита. Последний бит в знаковых типах обозначают знак числа, означающий: например:

0001 - это 1 знак и без знака 1001 составляет -1 подписанные и неподписанные 9

(я избегал всей проблемы дополнения для ясности объяснения! Это не совсем то, как Инты представлены в памяти!)

Вы можете себе представить, что это имеет значение, чтобы знать, если вы сравниваете с -1 или с +9. Во многом бывает, программисты просто слишком ленивы объявить подсчет ints без знака (раздувание головки цикла for f. i.) Обычно это не проблема, потому что с ints вы должны считать до 2^31 пока твой знак не укусит тебя. Вот почему это только предупреждение. Потому что нам лень писать "unsigned" вместо "int".

В крайних диапазонах беззнаковый int может стать больше, чем int.
Поэтому компилятор выдает предупреждение. Если вы уверены, что это не проблема, не стесняйтесь приводить типы к одному типу, чтобы предупреждение исчезло (используйте приведение C++, чтобы их было легко обнаружить).

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

Или используйте эту библиотеку заголовков и напишите:

// |notEqaul|less|lessEqual|greater|greaterEqual
if(sweet::equal(valueA,valueB))

И не заботьтесь о подписанных/неподписанных или разных размерах