Почему GCC оптимизирует доступ к переменной глобального массива

#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. Я знаю, что это не имеет смысла, я все убрал для удобства чтения :П. И я снял слишком много, на самом деле это пустота с передачей результата другой функции. Но, да, мне нужно чаще прижиматься к стене. Это такая простая ошибка, которую я упустил из виду.