Почему я могу передать int со значением больше 127 в массив символов, но не напрямую? - PullRequest
0 голосов
/ 08 ноября 2018

Я понимаю, что значение символа не может быть представлено как 176, но некоторые байтовые системы не имеют знака (0-255), в то время как другие являются знаковыми (от -128 до 127).В этом случае я работаю с unsigned, поэтому я просто хотел создать простой массив байтовых сообщений, но я получаю эту ошибку, когда пытаюсь разместить более высокое значение, чем 127, но если я сначала объявляю его как int, то это позволяет избежать ошибки,Может кто-нибудь подробно объяснить, почему это работает?

Способ 1: не работает.Я получаю эту ошибку: сужение преобразования '176' из 'int' в 'char'

char m1[3]{ 176, 118, 1 };

Метод 2: Это работает

int b1 = 176;
char m1[3]{ b1, 118, 1 };

Ответы [ 3 ]

0 голосов
/ 08 ноября 2018

Типичные диапазоны символов в большинстве систем:

  • Тип: символ , диапазон: -127 до 127 или 0 до 255
  • Тип: символ без знака , диапазон: от 0 до 255
  • Тип: знаковый символ , диапазон: -127 до 127

Является ли char со знаком или без знака определяется реализацией. Вы можете проверить, используя std::is_signed<char>(). В вашем случае это неявно подписано , и компилятор обнаружил, что вы неявно преобразуете из положительного целого числа (10110000 = 176) в значение со знаком (где 10110000 равно -90). Предупреждение не будет сгенерировано, если значения меньше или равны 127 (попробуйте!). Если вы хотите избежать неявного преобразования (сужающего преобразования), вы можете явно указать, что оно unsigned :

unsigned char m1[3]{ 176, 118, 1 };

Если вы хотите использовать подписанных символов , вам лучше использовать модификатор signed, а не полагаться на реализацию:

signed char m1[3]{ b, 118, 1 }; // where b is less than or equal to 127

При {} -инициализации компилятор должен - по крайней мере - диагностировать его как предупреждение, что делает программу плохо сформированной. Но опять же, это зависит от компилятора и используемых опций. Если вы перейдете на https://gcc.godbolt.org/ и скомпилируете свой код с разными компиляторами / опциями, вы увидите разницу.

Некоторые примеры:

Для следующего кода:

char m1[3] = {176, 118, 1 };
  • с x86-64 gcc 6.1 вы получаете ошибку: error: narrowing conversion of '176' from 'int' to 'char' inside { } [-Wnarrowing]. Однако, когда вы используете флаг -Wno-narrowing, вы его не получаете, но, тем не менее, ваша программа плохо сформирована, и вы этого не хотите.
  • с x86-64 gcc 4.8.1 вы не получите ни ошибок, ни предупреждений. Однако, используя, например, опции -Wno-narrowing или -Werror=narrowing, вы можете видеть, что он отклонен с предупреждением или ошибкой соответственно.

С кодом:

int b1 = 176;
char m1[3] = {b1, 118, 1 };

Вы получаете различное поведение с разными версиями компилятора, но, тем не менее, программа некорректна.

В общем, использование таких опций, как -Wnarrowing, -Wall или -Werror=narrowing с любой версией (я думаю, я не проверял все из них) компилятора, должно указывать на сужающее преобразование, что означает что ваша программа плохо сформирована, и вы этого не хотите.

Надеюсь, это поможет!

0 голосов
/ 08 ноября 2018

Оба случая плохо сформированы, но, как я объясняю в мой ответ здесь неправильно сформирован, требует только диагностики. Является ли эта диагностика предупреждением или ошибкой, зависит от реализации. Так что это очень хороший результат.

Например, для этого случая gcc выдает ошибку для первого, но только предупреждение для второго ( см. В прямом эфире на Годболте ):

error: narrowing conversion of '176' from 'int' to 'char'  [-Wnarrowing]
    2 |     char m1[3]{ 176, 118, 1 };
      |                             ^
warning: narrowing conversion of 'b1' from 'int' to 'char' [-Wnarrowing]

    5 |    char m2[3]{ b1, 118, 1 };
      |                           ^ 

Это разрешено стандартом, я процитирую соответствующий раздел из этого отчета об ошибке gcc по этому:

Стандарт требует только, чтобы «соответствующая реализация выдала хотя бы одно диагностическое сообщение», поэтому компиляция программы с предупреждением разрешена. Как сказал Эндрю, -Werror = сужение позволяет вам сделать ошибку, если хотите.

G ++ 4.6 выдавал ошибку, но она была намеренно изменена на предупреждение для 4.7, потому что многие люди (включая меня) обнаружили, что сужение конверсий является одной из наиболее часто встречающихся проблем при попытке компилировать большие кодовые базы C ++ 03 как C + +11. Ранее правильно сформированный код, такой как char c [] = {i, 0}; (где я только когда-либо буду в пределах диапазона char) вызвало ошибки и должно было быть изменено на char c [] = {(char) i, 0}

С помощью gcc и clang вы можете превратить все предупреждения в ошибки, используя -Werror.

0 голосов
/ 08 ноября 2018

При использовании фигурных скобок для инициализации (так называемая «равномерная инициализация») сужающие преобразования не допускаются. В противном случае они есть, и значение просто молча усекается.

У большинства компиляторов есть параметры предупреждений, которые вы можете включить, которые будут перехватывать многие (но не все) случаи, когда происходит усечение. Обычно они также имеют параметры, которые могут превратить такие предупреждения в ошибки. Вы должны использовать эти опции.

Если вы хотите работать с байтами, тогда std :: byte , возможно, является правильным типом для использования. Или (если вы не можете использовать это) std :: uint8_t .

...