#c #gcc #optimization
Вопрос:
У меня проблема с моим кодом, так как, похоже, мышление gcc не совпадает с моим. Вот упрощенная версия проблемы.
struct foo { uint16_t var; }; struct foo foos[7]; struct foo foo; uint16_t update_visual_alert(uint8_t match) { for(uint8_t i; i lt; 4;i ) { uint16_t color = 5; if(match) { color = foos[i].var; } return color; } return 1; }
С -01 GCC сокращает
color = foos[i].var;
Но он работает с доступом к одной глобальной переменной.
color = foo.var;
Очевидно, что он не вырезан без оптимизации или с изменчивым ключевым словом, как по цвету, так и по структуре массива. Но добавив
printf("%d",amp;color);
Заставляет gcc также получать к ним доступ. Почему gcc оптимизирует его?
Это упрощенная версия алгоритма, который я использую в своем приложении, и это вызвало у меня ошибку с игнорированием значений.
Комментарии:
1.
for(uint8_t i; i lt; 4;
= доступ к неинициализированной переменной = может случиться все, что угодно2. Спасибо, похоже, что это основная причина. Был так сосредоточен на странном поведении, что не заметил такой ошибки.
3. @BullNuk добро пожаловать.
gcc -Wall
который включает-Wuninitialized
в себя вашего друга, если по какой-то причине вы не можете скомпилировать с предупреждениями, у вас также есть отличный инструмент:cppcheck --enable=all .
(несмотря на название cpp, он также работает для C)4. Если вы собираетесь использовать godbolt (что является хорошей идеей), пожалуйста, укажите ссылку, которой можно поделиться, с помощью кнопки «Поделиться». Тогда люди действительно смогут опробовать код и внести свои собственные незначительные изменения, которые они не могут сделать с помощью простого снимка экрана.
Ответ №1:
gcc просто предотвратил UB. Всегда используйте-Wall -Wextra в качестве минимального уровня предупреждения. С помощью этих параметров gcc генерирует предупреждение:
lt;sourcegt;: In function 'update_visual_alert': lt;sourcegt;:14:22: warning: 'i' is used uninitialized [-Wuninitialized] 14 | for(uint8_t i; i lt; 4;i ) | ~~^~~ Compiler returned: 0
Без UB gcc делает то, что вы хотите (используя -O3
):
struct foo { uint16_t var; }; struct foo foos[7]; struct foo foo; uint16_t update_visual_alert(uint8_t match) { for(uint8_t i; i lt; 4;i ) { uint16_t color = 5; if(match) { color = foos[i].var; } return color; } return 1; }
update_visual_alert: mov eax, 5 ret
uint16_t update_visual_alert1(uint8_t match) // no UB { for(uint8_t i = 0; i lt; 4;i ) { uint16_t color = 5; if(match) { color = foos[i].var; } return color; } return 1; }
update_visual_alert1: test dil, dil mov eax, 5 cmovne ax, WORD PTR foos[rip] ret foo:
Кстати, ваш цикл вообще не имеет смысла, так как он всегда будет принимать значение первого элемента массива. Также return 1
никогда не будет достигнуто.
Комментарии:
1. На самом деле цикл всегда будет возвращать значение 0-го элемента массива (если
match
это правда), потомуreturn color;
что это всегда будет происходить на первой итерации.2. @NateEldridge хорошее место 🙂
3. Я знаю, что это не имеет смысла, я все убрал для удобства чтения :П. И я снял слишком много, на самом деле это пустота с передачей результата другой функции. Но, да, мне нужно чаще прижиматься к стене. Это такая простая ошибка, которую я упустил из виду.