Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Как int преобразуется в char и как char преобразуется в int?

Автор вопроса: Igor LiferenkoОтветов: 3

В следующем примере выводится битовое представление byte со всеми единицами:

#include <stdio.h>
int main (void)
{
  char c = 255;
  char z;
  for (int i = 7; i >= 0; i--) {
    z = 1 << i;
    if ((z & c) == z) printf("1"); else printf("0");
  }
  printf("\n");
  return 0;
}

Выход 11111111

Теперь мы меняем char c на int c, чтобы пример стал таким:

#include <stdio.h>
int main (void)
{
  int c = 255;
  char z;
  for (int i = 7; i >= 0; i--) {
    z = 1 << i;
    if ((z & c) == z) printf("1"); else printf("0");
  }
  printf("\n");
  return 0;
}

Теперь выход 01111111.

Почему выход другой?

ОБНОВЛЕНИЕ

Скомпилируйте следующее test.c:

#include <stdio.h>
int main(void)
{
  char c=-1;
  printf("%c",c);
  return 0;
}

$ gcc test.c
$ ./a.out | od -b
0000000 377
0000001

Вывод 377, что означает, что glibc противоречит gcc, потому что подписанный символ автоматически преобразуется в беззнаковый. Почему такие сложности? Разумно иметь char без знака по умолчанию. Есть какая-то конкретная причина, почему нет?

  • Если вам нужна определенная разрядность, используйте типы фиксированной разрядности из stdint.h. И при смещении, как правило, лучше использовать беззнаковые типы, потому что определенные сдвиги/значения для целых чисел со знаком определяются реализацией или даже вызывают неопределенное поведение. too honest for this site 08.11.2016

ctype-conversionimplicit-conversion

Ответы

Ответ 1 · рейтинг 1 · Lundin

  • Первая проблема здесь - тип char. Этот тип никогда не следует использовать для хранения целочисленных значений, поскольку он имеет определяемую реализацией подпись. Это означает, что он может быть как подписанным, так и неподписанным, и вы получите разные результаты на разных компиляторах. Если char не имеет знака в данном компиляторе, то этот код будет вести себя так, как вы ожидали.

    Но если char подписано, char c = 255; приведет к слишком большому значению. Затем значение 255 будет преобразовано в число со знаком некоторым специфичным для компилятора способом. Обычно путем перевода значения необработанных данных в эквивалент дополнения до двух.

    Хорошие компиляторы, такие как GCC, предупредят об этом: "переполнение при неявном преобразовании констант".

    Устраните эту ошибку, никогда не используя char для хранения целых чисел. Вместо этого используйте uint8_t.

  • Та же проблема возникает, когда вы пытаетесь сохранить 1 << 7 внутри типа char, подписанного вашим компилятором. Когда это произойдет, z станет отрицательным значением (-128).

  • В выражении z & c оба операнда являются целочисленными, преобразованными в тип int. Это происходит в большинстве выражений C всякий раз, когда вы используете небольшие целочисленные типы, такие как char.

    Оператор & не заботится о том, подписаны ли операнды или нет, он будет выполнять побитовое И над значениями "необработанных данных" переменных. Когда c является знаком char и имеет необработанное значение 0xFF, вы получите отрицательный результат с установленным битом знака. Значение -1 на компьютерах с дополнением до двух.

Итак, чтобы ответить, почему вы получаете разные результаты в двух случаях:

Когда вы переключаете тип на int, значение 255 помещается внутри c без преобразования в отрицательное значение. Результатом операции & также будет int, и бит знака этого int никогда не будет установлен, в отличие от случая char.

При выполнении -128 & 255 результатом будет 128 (0x80). Это положительное целое число. Однако z является отрицательным целым числом со значением -128. Он будет повышен до int оператором ==, но знак будет сохранен. Поскольку 128 не равно -128, старший бит будет напечатан как ноль.

Вы получите тот же результат, если переключите char на uint8_t.

Ответ 2 · Sumit Gemini

для char в int вы должны определить char как unsigned, потому что по умолчанию char или любой другой тип рассматривается как singed.

 int main (void)
 {
 int c = 255;
 unsigned char z;
 int i;
 for (i = 7; i >= 0; i--) {
 z = 1 << i;
 if ((z & c) == z) printf("1"); else printf("0");
 }
 printf("\n");
 return 0;
 }

Ответ 3 · Breaking not so bad

(изменить, чтобы уточнить "подписано по умолчанию")

В первом листинге (z == c) проверяет два char ; однако во втором листинге (z == c) проверяет один char и один int.

Для выполнения операций & и == между char и int компилятор расширяет char до размера int. эм>.

Что касается бита 7 (8-й):

Если ваш компилятор считает char беззнаковым по умолчанию, условие

(((int)(128) & (int)255) == (int)128)

будет отображаться true, и будет напечатано 1. Однако в вашем случае результат ложный, и отображается 0.

Причина, скорее всего, в том, что ваш компилятор считает char подписанным (например, gcc по умолчанию). В этом случае char, установленный в 1 << 7, на самом деле -128, а в int (не менее двух байтов) 255 является положительным.

(char)-128, расширенное до int, равно (int)-128, поэтому условие

if ((z & c) == z)

читает

if (((int)(-128) & (int)255) == (int)-128)

что неверно в данном случае.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы