Контекст
Мы переносим код C, который был изначально скомпилирован с использованием 8-разрядного C компилятора для микроконтроллера PI C. Распространенная идиома, которая использовалась для предотвращения перехода без нуля глобальных переменных (например, счетчиков ошибок) к нулю, такова:
if(~counter) counter++;
Битовый оператор здесь инвертирует все биты и оператор Истинно, только если counter
меньше максимального значения. Важно, что это работает независимо от размера переменной.
Проблема
Сейчас мы нацелены на 32-разрядный процессор ARM, используя G CC. Мы заметили, что один и тот же код дает разные результаты. Насколько мы можем судить, похоже, что операция побитового дополнения возвращает значение, которое отличается от ожидаемого. Чтобы воспроизвести это, мы скомпилируем в G CC:
uint8_t i = 0;
int sz;
sz = sizeof(i);
printf("Size of variable: %d\n", sz); // Size of variable: 1
sz = sizeof(~i);
printf("Size of result: %d\n", sz); // Size of result: 4
В первой строке вывода мы получим то, что ожидаем: i
равен 1 байту. Однако побитовое дополнение i
на самом деле составляет четыре байта , что вызывает проблему, поскольку сравнение с этим в настоящее время не даст ожидаемых результатов. Например, если вы выполняете (где i
- правильно инициализированный uint8_t
):
if(~i) i++;
Мы увидим i
«обтекание» от 0xFF до 0x00. Это поведение отличается в G CC по сравнению с тем, когда он работал так, как мы предполагали в предыдущем компиляторе и 8-битном микроконтроллере PI C.
Мы знаем, что можем решить эту проблему путем приведения типа Итак:
if((uint8_t)~i) i++;
Или
if(i < 0xFF) i++;
Однако в обоих этих обходных путях размер переменной должен быть известен и подвержен ошибкам для разработчика программного обеспечения. Такого рода проверки верхних границ происходят по всей кодовой базе. Существует несколько размеров переменных (например, uint16_t
и unsigned char
et c.), И изменение их в другой работающей кодовой базе - это не то, чего мы ожидаем.
Вопрос
Правильно ли наше понимание проблемы, и есть ли варианты решения этой проблемы, которые не требуют повторного посещения каждого случая, где мы использовали эту идиому? Верно ли наше предположение, что такая операция, как побитовое дополнение, должна возвращать результат того же размера, что и операнд? Кажется, что это сломается, в зависимости от архитектуры процессора. Я чувствую, что принимаю сумасшедшие таблетки и что C должно быть немного более портативным, чем это. Опять же, наше понимание этого может быть неправильным.
На первый взгляд это может показаться не такой уж большой проблемой, но эта ранее работавшая идиома используется в сотнях локаций, и мы стремимся понять это, прежде чем продолжить дорогостоящие изменения.
Примечание: Здесь есть, похоже, похожий, но не точный повторяющийся вопрос: Битовая операция над символом дает 32-битный результат
Я не видел реальной сути обсуждаемой там проблемы, а именно, размер результата побитового дополнения отличается от того, что передано оператору.