Почему C не запускает сравнение беззнакового целого с отрицательным значением? - PullRequest
4 голосов
/ 02 июня 2011

Рассмотрим этот код C:

#include "stdio.h"

int main(void) {

    int count = 5;
    unsigned int i;

    for (i = count; i > -1; i--) {
        printf("%d\n", i);
    }
    return 0;
}

Мое наблюдение / вопрос: цикл никогда не выполняется. Но если я изменю тип данных i с unsigned int на int, все будет работать как положено.

Я думал о неподписанных целочисленных значениях как о значениях, которые «оборачиваются», когда вы пытаетесь отнять их. Таким образом, когда я равняюсь нулю и вычитаю 1, он оборачивается в UINT_MAX. И поскольку его значение никогда не бывает отрицательным, это фактически будет бесконечный цикл. (И это именно то, что происходит, когда я меняю сравнение с i> -1 на i> = 0.)

Где-то в моей логике есть ошибка, поскольку цикл никогда не выполняется, если я не подписан, и я сравниваю его с -1. Либо компилятор каким-то образом оптимизирует его, либо значения времени выполнения ведут себя не так, как я ожидаю.

Почему цикл не запускается?

Ответы [ 4 ]

18 голосов
/ 02 июня 2011

В i > -1 -1 преобразуется в unsigned int, в результате чего получается значение UINT_MAX.i никогда не бывает больше этого значения, поэтому тело цикла никогда не выполняется.

Вы можете обнаружить, что можете убедить свой компилятор предупредить вас об этом: использовать выражение всегда истинно или всегда ложно вусловный контекст.Но это не помогло бы вам, если бы вы написали i > -2, поэтому вы также можете обнаружить, что можете включить предупреждение для всех сравнений со смешанными знаками.

Обратите внимание, что в C арифметика равна всегда выполняется с операндами одинакового типа.Это включает в себя сравнения, но IIRC не операторы смены.Если операнды имеют другой тип, как в этом случае, то по крайней мере один из них преобразуется, чтобы сделать их одним и тем же типом.Правила определения типа пункта назначения приведены в 6.3.1.1/2 и 6.3.1.8/1.

.
3 голосов
/ 02 июня 2011

Когда вы смешиваете операнды со знаком и без знака одинаковой ширины в «симметричной по типу» двоичной операции (например, +, * или > в вашем примере), тип без знака «выигрывает» и операцияоценивается в неподписанном домене.Т.е. подписанный операнд преобразуется в неподписанный тип.

В вашем примере целочисленная константа имеет тип signed int, тогда как i имеет тип unsigned int.Операнды имеют одинаковую ширину, поэтому в вашем примере i > -1 интерпретируется как i > (unsigned) -1, что эквивалентно i > UINT_MAX.Вот почему ваш цикл никогда не выполняется.

0 голосов
/ 02 июня 2011

Независимо от того, имеете ли вы дело с числами без знака или со знаком, -1 всегда будет компилироваться как 0xffffffff.Процессор имеет и флаги сравнения со знаком и без знака.Сравнивая это число с 5, подписанные флаги будут обрабатывать его как -1 и говорить, что оно меньше, но беззнаковые флаги будут обрабатывать его как большое число и говорить, что оно больше.Поскольку это число также совпадает с UINT_MAX, ваше сравнение будет ложным для всех чисел без знака.

0 голосов
/ 02 июня 2011

-1 становится UINT_MAX в сравнениях без знака.Поскольку ни одно число не превышает этого значения, условие цикла никогда не выполняется, и цикл никогда не вводится.

Если вы измените его на i >= 0, это должно работать так, как ожидается.вероятно, просто не следует использовать unsigned в этом случае: -)

...