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

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

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

Упражнение - В примере программы авторы используют 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 ответов

Решение

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

Компиляторы предупреждают о сравнении типов со знаком и без знака, потому что диапазоны значений со знаком и без знака отличаются, и когда они сравниваются друг с другом, результаты могут быть удивительными. Если вам нужно провести такое сравнение, вам следует явно преобразовать одно из значений в тип, совместимый с другим, возможно, после проверки, чтобы убедиться, что преобразование допустимо. Например:

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 книги), как вы правильно отметили.

Что делает этот пример более сложным, так это то, что различные переменные заполнения (padsides и padtopbottom), а также все счетчики также должны быть изменены на 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. Во многих случаях программистам просто лень объявлять подсчет целых чисел беззнаковым (вздутие fi заголовка цикла for). Обычно это не проблема, потому что с целыми числами нужно считать до 2^31, пока ваш знаковый бит не укусит вас. Вот почему это только предупреждение. Потому что нам лень писать "unsigned" вместо "int".

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

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

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

8 битов: -1 подписано - это те же биты, что и 255 без знака 16 битов: -1 подписано - это те же биты, что и 65535 без знака и т. Д.

Итак, если у вас есть следующий код:

int fd;
fd = open( .... );

int cnt;
SomeType buf;

cnt = read( fd, &buf, sizeof(buf) );

if( cnt < sizeof(buf) ) {
    perror("read error");
}

вы обнаружите, что если вызов read(2) завершится неудачно из-за того, что дескриптор файла станет недействительным (или из-за какой-либо другой ошибки), этот cnt будет установлен в -1. При сравнении с sizeof(buf), значением без знака, оператор if() будет ложным, поскольку 0xffffffff не меньше, чем sizeof() для некоторой (разумной, не придуманной, чтобы быть максимального размера) структуры данных.

Таким образом, вы должны написать выше, если, чтобы удалить подписанное / неподписанное предупреждение как:

if( cnt < 0 || (size_t)cnt < sizeof(buf) ) {
    perror("read error");
}

Это просто громко говорит о проблемах.

1.  Introduction of size_t and other datatypes was crafted to mostly work, 
    not engineered, with language changes, to be explicitly robust and 
    fool proof.
2.  Overall, C/C++ data types should just be signed, as Java correctly
    implemented.

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

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

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

и не волнует подписанные / неподписанные или разных размеров

Другие вопросы по тегам