Как я могу исправить предупреждения типа "сравнение между подписанным и неподписанным"?
Мне рекомендуется использовать следующие опции с GCC, так как это помогает избежать множества распространенных ошибок. Он включает кучу предупреждений и -Werror
превращает их в ошибки.
gcc -pedantic -W -Wall -Wextra -Wshadow -Wstrict-overflow=5 -Wwrite-strings -std=c99 -Werror
Учитывая следующий тестовый код:
#include <stdio.h>
int main(void)
{
int arr[8]={0,10,20,30,40,50,60,70};
int x;
printf("sizeof(arr): %d\n", sizeof(arr));
printf("sizeof(int): %d\n", sizeof(int));
for(x = 0; x < sizeof(arr)/sizeof(int); x++)
{
printf("%d\n",arr[x]);
}
return 0;
}
Я получаю это:
test.c:11: error: comparison between signed and unsigned
Я знаю, что один из способов исправить это - отключить предупреждения, но они не заставили меня использовать эти настройки, чтобы отключить их в конце.
Другой способ - передать материал, но мне сказали, что кастинг устарел.
Кроме того, я мог бы сделать x в unsigned int
:
unsigned x;
Но это не решает общую проблему, когда мне приходится сравнивать знаковые значения с неподписанными значениями, используя эти параметры компилятора. Есть ли более чистый способ вместо кастинга?
Ответы
Ответ 1
Это действительно зависит от типа данных. Этого можно избежать, неявным образом приведя значения к типу, который содержит надмножество всех значений, доступных в подписанном и неподписанном типе. Например, вы можете использовать 64-битное значение знака для сравнения 32-битного знака и знакового 32-битного значения.
Однако это угловой случай, и вам нужно выполнять операции, такие как проверка размера ваших типов. Лучшее решение - использовать один и тот же тип для обоих операндов.
Если вы должны бросить, подумайте, что вы можете вызвать переполнение и подумать, важно ли это для вашего приложения или нет.
Ответ 2
Заменить
int x;
/* ... */
for(x=0;x<sizeof(arr) / sizeof(int);x++)
по
for(size_t x=0;x<sizeof(arr) / sizeof(int);x++)
Но это не решает общую проблему, когда мне приходится сравнивать знаковые значения с неподписанными значениями, используя эти параметры компилятора. Есть ли более чистый способ каста?
В таких случаях попытайтесь выяснить, может ли число со знаком иметь значение, которое вызовет переполнение. Если нет, вы можете игнорировать это предупреждение. В противном случае приведение к неподписанному типу (если это тот же размер или больше, чем подписанный компонент) достаточно хорош.
Ответ 3
Суть в том, что сравнение значений под знаком и без знака допускает некоторые странные случаи. Рассмотрим, например, то, что происходит в длину без знакового массива, больше, чем максимум, который может быть представлен подписанным int. Подписанный счетчик переполнения (оставшийся "меньше" размера массива), и вы начинаете обращаться к памяти, которую вы не хотели...
Компилятор генерирует предупреждение, чтобы убедиться, что вы о них думаете. Использование -Werror
способствует предупреждению об ошибке и остановке компиляции.
Будьте предельно осторожны в выборе соответствия вашим типам или отбросите проблему, если вы уверены, что она не применяется, или избавиться от -Werror
и сделать ее политикой для устранения всех предупреждений с помощью исправления или объяснение...
Ответ 4
одним из вариантов будет дополнительный флаг "-Wno-sign-compare":)
Ответ 5
Одним из способов решения проблемы было бы выборочно отключить это предупреждение в этом специальном случае.
GCC игнорирует диагностику pragma "-Wsomething"
// Disable a warning for a block of code:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
// ... Some code where the specified warning should be suppressed ...
#pragma GCC diagnostic pop
Последние версии GCC (на самом деле я не уверен, с тех пор, когда 4.8.x должен его поддерживать) покажите соответствующую опцию -Wsomething. Это важно, поскольку большинство параметров предупреждения не заданы явно, а en bloc с такими параметрами, как -Wall.
Сообщение об ошибке будет выглядеть так:
readers.c: In function ‘buffered_fullread’:
readers.c:864:11: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
if(got < sizeof(readbuf)) /* That naturally catches got == 0, too. */
Часть [-Werror = sign-compare] сообщает вам. Вы можете использовать "Wsign-compare" для "Wsomething" для подавления предупреждения.
И, конечно же, вы должны делать это только там, где это уместно (это не совсем помогает читаемости), например. когда требуется точное поведение, о котором предупреждает компилятор (или, если вы не можете вводить большие изменения в базе кода).
Ответ 6
test.c:11: error: comparison between signed and unsigned
Вы можете объявить x как unsigned int, так как size_t не имеет знака
ИЗМЕНИТЬ:
Если вы не хотите бросать и не хотите объявлять его как unsigned, я не думаю, что там что-то делать.
Возможно, побитовые операции - это способ его решения, удаляя бит знака. Я должен сказать, что ИМО очень сомнительна.
Ответ 7
Мы подавляем это предупреждение в наших компиляторах Visual Studio, поскольку это происходит много и почти никогда не означает ничего значительного. Конечно, не все стандарты кодирования позволяют это.
Вы можете согласовать типы (объявляя переменные как size_t или unsigned int вместо int, например), или вы можете использовать, или вы можете изменить свою линию компиляции. Что об этом.
Ответ 8
Независимо от дилеммы устаревания литья, я все же предлагаю отделить логику от цикла for.
int range = (int)(sizeof(arr) / sizeof(int));
int x;
for(x = 0; x < range; x++)
{
printf("%d\n", arr[x]);
}
Хотя это использует литье, которое, как вы сказали, устарело, оно очищает место, где происходит кастинг. В общем, я советую не вводить много логики в ваши объявления цикла for. Особенно в вашем случае, когда вы используете разделение size_t, которое (поскольку это целочисленное деление) может иметь возможность усечения ответа. Объявление цикла цикла должно быть чистым и не должно генерировать ошибок. Ваше кастинг происходит в другом месте, что означает, что если вы хотите изменить способ создания диапазона, вам не нужно гадать, чтобы сделать объявление объявления еще длиннее.