На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> неявное приведение типов
    Всем хай!

    Есть такой ( вроде бы ) простейший код на языке С ( С89 ):
    ExpandedWrap disabled
      #include <stdio.h>
      #include <stdlib.h>
       
      int main( void )
      {
          unsigned int a;
          int b;
       
          a = 5;
          b = -11;
       
          printf( "b > a: %d\n\n", b > a );
       
          system( "pause" );
          return EXIT_SUCCESS;
      }


    хотелось бы пошагово его разобрать на битовом уровне

    Оговорка: пусть для упрощения писанины типы данных unsigned int и int 2байтовые ( по 16 бит )
    ------------------------------------------------------------------------------------------------
    в этой строке
    ExpandedWrap disabled
          unsigned int a;

    резервируется память под переменную a в размере 16 бит ( 2 байта )

    в этой строке
    ExpandedWrap disabled
          int b;

    резервируется память под переменную b в размере 16 бит ( 2 байта )

    в этой строке
    ExpandedWrap disabled
          a = 5;

    16 бит принимают заданное значение. Тип данных переменной а беззнаковый, т е старший бит участвует
    a = 00000000 00000101 - двоичное представление в памяти ЭВМ

    в этой строке
    ExpandedWrap disabled
          b = -11;

    16 бит принимают заданное значение. Тип данных переменной b знаковый, т е старший бит участвует и будет равен 1
    также отр.числа хранятся в доп.коде, который получается из прямого инвертированием бит + 1
    10000000 00001011 --> 11111111 11110100 --> 11111111 11110101
    b = 11111111 11110101 - двоичное представление в памяти ЭВМ

    в итоге
    a = 00000000 00000101
    b = 11111111 11110101
    --------------------------

    и затем последняя, самая сложная)) строка кода:
    ExpandedWrap disabled
          printf( "b > a: %d\n\n", b > a );

    сначала компилятор вычисляет выражение b > a + он замечает, что у них РАЗНЫЕ типы данных. По правилам, заложенным где-то в недрах его он все приводит к беззнаковому типу, т е к типу данных unsigned int.
    То есть теперь компилятор смотрит на эти биты 11111111 11110101 ( это переменная b ) с тз типа данных unsigned int и это число 2^16 - 11 = 65 525

    происходит сравнение:
    65 525 > 5 - ну как бы да)
    это лог. операция и ее результатом является значение TRUE/FALSE. При этом для языка Си FALSE это ТОЛЬКО значение 0, а TRUE - все остальное целочисленное ( даже отр.числа )

    при печати стоит модификатор %d, т е давит знаковый целочисленный тип. Что распечатается в ответе? Печатается 1, ну в принципе это логично. Хотя, наверное, могло распечататься любое целое число, не равное 0 ( 0 отдан ТОЛЬКО false ). Но вот всегда в таких ситуациях печатается 1; не 2, не 43, не -5392, а именно 1. Почему? - это небольшой доп. вопрос.
    -----------------------------------------------------

    все ли верно расписал выше или есть какие-то грубые нестыковки?
    спс. за внимание, надеюсь, не сильно утомил!)
      Цитата FasterHarder @
      10000000 00001011

      При переходе от без знакового числа к числу в дополнительном коде бит знака возникает сам в результате инверсии. Его не нужно описывать отдельно.
      ExpandedWrap disabled
        int b = 11 // 0000`0000`0000`1011
        b = -b // инверсия 1111`1111`1111`0100 и добавление единицы 1111`1111`1111`0101 - старший разряд без особой обработки стал равен знаку числа.
        b = -b // инверсия 0000`0000`0000`1010 и добавление единицы 0000`0000`0000`1011 - старший разряд без особой обработки опять стал равен знаку числа.
         
        //Тоже самое справедливо для значения 0
        b = 0 // 0000`0000`0000`0000
        b = -b // инверсия 1111`1111`1111`1111 и добавление единицы 0000`0000`0000`0000
         
        //И для значения 0x8000
        b = 0x8000 // 1000`0000`0000`0000
        b = -b // инверсия 0111`1111`1111`1111 и добавление единицы 1000`0000`0000`0000


      После операции сравнения используется команда записи значения флага в регистр (seta - при сравнении без знаковых/setg - после сравнения знаковых), которая дает значения 0 или 1 в зависимости от операции сравнения. т.е. на вывод поступает именно значение типа bool (0 = false / 1 = true) после операции сравнения.
      Сообщение отредактировано: macomics -
        Цитата FasterHarder @
        Но вот всегда в таких ситуациях печатается 1; не 2, не 43, не -5392, а именно 1. Почему?
        Открываем стандарт С99:

        Цитата
        6.3.1.2 Boolean type
        When any scalar value is converted to _Bool, the result is 0 if the value compares equal to 0; otherwise, the result is 1.
        Никакой магии.
          Dushevny, ответ - бомба! :lool:
            Цитата macomics @
            После операции сравнения используется команда записи значения флага в регистр (seta - при сравнении без знаковых/setg - после сравнения знаковых), которая дает значения 0 или 1 в зависимости от операции сравнения. т.е. на вывод поступает именно значение типа bool (0 = false / 1 = true) после операции сравнения.

            понятно, этого не знал, спс

            Цитата Dushevny @
            Открываем стандарт С99:

            ну я как бы это того писал, что мол:
            Цитата FasterHarder @
            код на языке С ( С89 ):

            надо сверить это для С89

            но macomics выше пояснил этот момент вроде как...

            зы: а вообще всех этих приведений надо по максам избегать, т к можно получить трудноуловимые проблемы
              Цитата FasterHarder @
              надо сверить это для С89
              Такого старого под рукой не было. А разве имеет смысл писать, ограничивая себя стандартом 33-летней давности?
              Цитата FasterHarder @
              а вообще всех этих приведений надо по максам избегать, т к можно получить трудноуловимые проблемы
              Вовсе необязательно. Достаточно просто знать, как они работают и исходник не будет перегружен явными приведениями типов в тех местах, где в них нет необходимости.
                Цитата Dushevny @
                Такого старого под рукой не было.
                Там то же, только без явного boolевого типа.
                Цитата
                Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false. The result has type int.
                ...
                The == (equal to) and the != (not equal to) operators are analogous to the relational operators except for their lower precedence.


                Добавлено
                P.S. Почему делается каст именно к беззнаковому при несовпадении знаковости, ябеспонятия. Но могу выдвинуть две гипотезы.
                • Очень часто, особенно на заре, char использовался как маленький int, и он был в большинстве своём знаковый, т.к. так однообразнее с другими типами, где отсутствие явной знаковости означает signed, а беззнаковым ему было быть особо без надобности, т.к. ASCII стандартизирована на коды 0-127. Если с ним смешивать операции с unsigned int-ом, то получился бы полный кавардак. a + b легко дало бы странный результат, ибо интутивно байт со значением 200 воспринимается отнюдь не как -56. Поэтому в частности при смене не только знака, но и длины сначала меняется длина и только потом знаковость. Так при смене длины сохраняется исходное значение до того, как сменится интерпретация его знакового бита. Потенциально это приводит к меньшему количеству странностей в поведении.
                • Операции с беззнаковыми целыми всегда работают по модулю 2N, так что ситуация переполнения в принципе невозможна. Операции со знаковыми целыми этого свойства лишены, поэтому переполнения потенциально могут привести к исключениям. И я даже знаю пару таких процессоров. Стандарт тут чётко говорит за implementation defined behavior. В случае каста к беззнаковому такие системы при переполнении всё равно отрапортуют об исключении, но оно будет одно, в противоположном случае более вероятно, что исключений будет много, большинство из которых лишние.
                Обе гипотезы сомнительные, так что относитесь к ним как к ИМХО.
                Сообщение отредактировано: Qraizer -
                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                0 пользователей:


                Рейтинг@Mail.ru
                [ Script execution time: 0,0306 ]   [ 16 queries used ]   [ Generated: 9.12.22, 23:03 GMT ]