озадачен предупреждением компилятора, которое предполагает, что составное назначение int8_t продвигается к int

Обычно я могу понять причину предупреждения компилятора, но это кажется совершенно неправильным.

#include <stdint.h>    
uint8_t myfunc(uint8_t x,uint8_t y)
{
    x |= y;
    return x;
}

Компилятор Intel с -Wall жалуется:

conversion from "int" to "uint8_t={unsigned char}" may lose significant bits
  x |= y;
    ^

Это правильно? Является ли приведенный выше код непереносимым и нестандартным?


person Mark Borgerding    schedule 03.05.2011    source источник
comment
Подводя итог комментариям в принятом ответе: компилятор технически прав в том, что существует преобразование из int в uint8_t, но это разочаровывающе глупо и неправильно в утверждении, что он может потерять важные биты.   -  person Mark Borgerding    schedule 06.05.2011


Ответы (4)


Это integer promotions на работе.

in

x |= y;

оба операнда оператора | повышаются до int

x = (int)x | (int)y;

затем результат преобразуется обратно в uint8_t с потерей точности.

person pmg    schedule 03.05.2011
comment
В предупреждении говорится, что могут быть потеряны важные биты. Это в корне неправильно. Эта операция не может привести к потере битов. - person R.. GitHub STOP HELPING ICE; 03.05.2011
comment
Компилятор, конечно, может оптимизировать код и сделать все это в 8 битах. Тем не менее, он все равно выдаст предупреждение. - person Mark Ransom; 03.05.2011
comment
@R, преобразование из int или unsigned int в uint8_t потенциально может привести к потере битов. Признание того, что нет никаких существенных битов, которые можно было бы потерять, было бы слишком большой проблемой для автора компилятора с очень небольшим выигрышем. - person Mark Ransom; 03.05.2011
comment
Компилятор, конечно, может выдавать предупреждения, если вы не придерживаетесь Стандартов кодирования GNU для отступа фигурных скобок, но большинство пользователей сочтут это невероятно раздражающим и неправильным. Точно так же это предупреждение неверно. Для выдачи предупреждений он слепо смотрит только на тип выражения, а не на возможный диапазон значений выражения. Если бы он использовал последнее, что имело бы гораздо больше смысла, он бы легко увидел, что потенциальный диапазон значений составляет 0-255, и заткнулся бы. - person R.. GitHub STOP HELPING ICE; 03.05.2011
comment
Обратите внимание, что есть много причин, по которым компилятор уже должен отслеживать потенциальный диапазон значений - например, если x|y используется в switch (x|y), вам определенно нужно знать, что x|y находится в диапазоне 0-255, чтобы использовать таблицу переходов без дополнительное условие для проверки диапазона... - person R.. GitHub STOP HELPING ICE; 03.05.2011
comment
@R..: Здесь это даже проще, чем анализ диапазона (который может потребовать анализа потока), gcc может понять, что некоторые операторы (|, ^, &) не увеличивают диапазон значения, и поэтому uint8_t | uint8_t обязательно вписывается в uint8_t. Похоже, они отложили этот анализ на потом (после продвижения типа) и не учли этого... Я, конечно, тоже поддержал бы любую форму облегченного анализа диапазонов, но это гораздо сложнее :) - person Matthieu M.; 03.05.2011
comment
Добавьте в список %, / и >> и, конечно же, обратите внимание, что все логические операторы имеют 1-битный диапазон. (В настоящее время он выдает предупреждение для uint8_t x = !foo;??) - person R.. GitHub STOP HELPING ICE; 03.05.2011
comment
Спасибо всем за информацию о том, что происходит за кулисами целочисленного продвижения. Я пришел к выводу, что предупреждения компилятора Intel даются со слишком грубой детализацией и слишком низким соотношением сигнал/шум, чтобы быть полезными. Тот факт, что я был в блаженном неведении об этой детали языка, который я интенсивно использовал в течение 20 лет, просто показывает, насколько это нелепо. Я не ожидаю такого рода предупреждений компилятора, если только я не попрошу педантичных предупреждений. - person Mark Borgerding; 03.05.2011
comment
Вы ошибаетесь в обоих операндах | оператор повышается до int. x = (int)x | (int)y;. На самом деле компилятор Intel делает это как x = x | (int)y;. - person osgx; 28.08.2011

Это верно. Оператор повышает аргумент(ы) до int. Подробнее см. на этой странице. Первое предложение начинается :

C не выполняет никаких арифметических операций с точностью меньше, чем int [...]

person unwind    schedule 03.05.2011

Значения x и y повышаются до int для вычислений, но предупреждение, тем не менее, является поддельным. Оператор | не может увеличить ширину результата в битах сверх ширины операндов, которые уже подходят для uint8_t, поскольку они были повышены с uint8_t. Подавляющее большинство вещей, которые помечает этот параметр предупреждения, являются полностью действительным и правильным кодом, и если вы не хотите тратить свое время на 100 подобных вопросов, я думаю, что лучше отключить или игнорировать эти предупреждения.

person R.. GitHub STOP HELPING ICE    schedule 03.05.2011
comment
Не отключайте предупреждения. В некоторых случаях вы по ошибке не были готовы к неявному продвижению. Скажем, вы случайно повысили статус неподписанного до подписанного в цикле, и вы хотели бы знать об этом. - person AJG85; 03.05.2011
comment
@ AJG85: Но вопрос в том, знаете ли вы об этом? - когда предупреждение скрыто, эта опция генерирует сотни других фиктивных предупреждений. Вот почему так важно, чтобы разработчики компиляторов исправляли предупреждения, чтобы они срабатывали только в ситуациях, когда что-то на самом деле может быть не так. Если они наводняют вас поддельными предупреждениями, вы либо (1) проигнорируете их, либо (2) отключите эту опцию. Я считаю вариант (2) немного лучше, так как он позволяет избежать сокрытия других типов предупреждений, которые могут иметь более высокое отношение сигнал/шум, среди потока фиктивных предупреждений. - person R.. GitHub STOP HELPING ICE; 03.05.2011
comment
Я бы задал такой вопрос: стоит ли бороться с компилятором экономии нескольких байтов памяти? Вероятно, вы могли бы ответить «да» только на это меньше, чем на то, чтобы знать, реально ли предупреждение и что с этим делать. У меня нет предупреждений, но я также не использую unsigned char при выполнении операций signed int. - person AJG85; 03.05.2011
comment
Ну, если бы вы просто использовали int для аргументов и возвращаемого значения, предупреждения в функции исчезли бы, но когда вы назначили его обратно в uint8_t, предупреждение снова появилось бы в этот момент... Я действительно не вижу никакого способа вокруг него, когда используются маленькие шрифты. - person R.. GitHub STOP HELPING ICE; 03.05.2011
comment
Если x или y отрицательные, будет ли продвижение увеличивать ширину в битах? - person osgx; 28.08.2011
comment
Переход от меньшего знакового типа к большему беззнаковому приведет к увеличению ширины значения. Переход от меньшего знакового типа к большему знаковому типу не приведет к увеличению ширины значения. - person R.. GitHub STOP HELPING ICE; 28.08.2011

Предупреждение компилятора может показаться бессмысленным, потому что операция не может производить более 8 бит, но это всего лишь подмножество более крупного класса операций, которые могут. Например, если вы заменили |= на +=, возможность переполнения становится вполне реальной.

Способ устранить предупреждение — сообщить компилятору, что вы сознательно отбрасываете биты с помощью приведения:

x = (uint8_t)(x | y);
person Mark Ransom    schedule 03.05.2011
comment
@R, конечно, просто ты уже знаешь, что все они равны нулю. Возможно, мне следует перефразировать это, чтобы сознательно игнорировать верхние биты. - person Mark Ransom; 03.05.2011