Re[3]: Откуда эта лютая любовь к знаковым целым?
От: Vamp Россия  
Дата: 06.05.20 01:15
Оценка: 3 (1) +5
V>Имхо, приведенная рекомендация не релевантна. Интересно услышать длинный ответ.

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


void baz(char );

void foo(char* begin, char* end) {
    for (int i = 0; i < (end - begin); ++i)
        baz(begin[i]);
    
}


Данный код предполагает, что расстояние между началом и концом уменьшается в 32 бита, и в рамках этого предположения работает отлично. Если заменить тип управляющей переменной на unsigned int, скорость кода заметно уменьшится. Связано это с тем, что компилятору придется контролировать переполнение на каждой операции (как правило, путем засовывания числа в отдельный короткий регистр), так как беззнаковые целые разрешают переполнение. Классическая рекомендация — использовать знаковое целое.

Очевидно, что проблему можно решить и более правильно — путем использования уместного типа, std::ptrdiff_t. Ирония заключается в том, что ptrdiff_t — знаковый тип.

Багов связанных с неправильным использованем беззнаковых целых много. Большинство из них связаны с тем, что стандартная арифметика на них не работает. Она, конечно, не работает и на знаковых числах на концах их диапазона, однако на беззнаковых она не работает в районе нуля. Из практики, средний код чаще работает с числами в районе нуля, чем с числами на конце диапазона знаковых целых.

В частности, классический пример: из школьной математики мы помним, что если A + B > C, то A > B — C. На беззнаковых целых это не работает при А = В = 2, С = 3. Подобное поведение, как правило, является довольно удивительным, потому что большинство практических программистов привыкли к школьной математике, которую они учили в четвертом классе, а не к программированию на С++, которое они учили в колледже.

Кроме того, в С++ в математических операциях, смешивающих знаковые и беззнаковые числа, знаковые приводятся к беззнаковым, после чего операция выполняется по правилам беззнаковых. Это тоже часто становится сюрпризом, классичский пример:

bool less()
{
    int a = -1;
    unsigned int b = 1;
    
    return a < b; // что вернет?
}



Подытоживая — сами по себе беззнаковые числа не являются потенциально опасными. Они ведут себя в строгом соответствии с правилами. Проблема в том, что правила эти противоречат школьной арифметике, и как правило, являются неприятной неожиданностью для многих программистов. В связи с этим, сообщество выработало рекомендации не пользоваться беззнаковыми целыми для математических операций, включая индексацию, и использовать их там, где их примение оправдано — в основном, битовые операции и "чистые" счетчики, например, счетчик циклов в процессоре.
Да здравствует мыло душистое и веревка пушистая.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.