Некоторые случайные вопросы C (магические и побитовые операторы ASCII) - PullRequest
5 голосов
/ 24 марта 2009

Я пытаюсь научиться программировать на С, изучал некоторые исходные коды, и есть некоторые вещи, которые я не понял, особенно в отношении побитовых операторов. Я читал некоторые сайты по этому поводу, и я вроде понял, что они делают, но когда я вернулся, чтобы посмотреть на эти коды, я не мог понять, почему и как они использовались.

Мой первый вопрос связан не с побитовыми операторами, а с некоторой магией ascii:

  1. Может кто-нибудь объяснить мне, как работает следующий код?

    char a = 3;
    int x = a - '0';
    

    Я понимаю, что это делается для преобразования char в int, однако я не понимаю логику, стоящую за ним. Почему / Как это работает?

  2. Теперь, что касается побитовых операторов, я чувствую себя действительно потерянным.

    • Что делает этот код?

      if (~pointer->intX & (1 << i)) { c++; n = i; }
      

      Я где-то читал, что ~ инвертирует биты, но я не вижу, что делает это утверждение и почему оно это делает.

      То же самое с этой строкой:

      row.data = ~(1 << i);
      
    • Другой вопрос:

      if (x != a)
        {
          ret |= ROW;
        }
      

      Что именно делает оператор | =? Из того, что я прочитал, | = ИЛИ, но я не совсем понимаю, что делает это утверждение.

      Есть ли способ переписать этот код, чтобы его было легче понять, чтобы он не использовал эти побитовые операторы? Я нахожу их очень запутанными, чтобы понять, так что, надеюсь, кто-то укажет мне правильное направление на понимание того, как они работают лучше!


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

И последнее: очевидно, никто не ответил, если будет «более чистый» способ переписать этот код так, чтобы его было легче понять, а может быть, и не на «уровне битов». Есть идеи?

Ответы [ 10 ]

17 голосов
/ 24 марта 2009

Это даст мусор:

char a = 3; 
int x = a - '0';

Это другое - обратите внимание на цитаты:

char a = '3'; 
int x = a - '0';

Тип данных char хранит число, идентифицирующее символ. Символы для цифр от 0 до 9 все находятся рядом друг с другом в списке кодов символов, поэтому, если вы вычтете код для «0» из кода для «9», вы получите ответ 9. Таким образом, это превратит цифру код символа в целое значение цифры.

(~pointer->intX & (1 << i))

Это будет интерпретироваться оператором if как истинное, если оно не равно нулю. Используются три разных побитовых оператора.

Оператор ~ переворачивает все биты числа, поэтому, если pointer->intX было 01101010, то ~pointer->intX будет 10010101. (Обратите внимание, что повсюду я иллюстрирую содержимое байта. Если бы это было 32-разрядное целое число, мне пришлось бы написать 32 цифры 1 и 0).

Оператор & объединяет два числа в одно число, обрабатывая каждый бит отдельно. Результирующий бит равен только 1, если оба входных бита равны 1. Поэтому, если левая сторона равна 00101001, а правая сторона равна 00001011, результат будет 00001001.

Наконец, << означает сдвиг влево. Если вы начнете с 00000001 и сдвинете его влево на три позиции, вы получите 00001000. Таким образом, выражение (1 << i) выдает значение, в котором бит i включен, а все остальные выключены. </p>

Собрав все вместе, он проверяет, выключен ли бит i (ноль) в pointer->intX.

Так что вы можете понять, что делает ~(1 << i). Если i равно 4, то значение в скобках будет 00010000, и поэтому все будет равно 11101111.

ret |= ROW;

Это эквивалентно:

ret = ret | ROW;

Оператор | похож на &, за исключением того, что результирующий бит равен 1, если любой из входных битов равен 1. Поэтому, если ret равно 00100000 и ROW равно 00000010, результат будет 00100010.

0 голосов
/ 24 марта 2009
  1. Я предполагаю, что вы имеете в виду char a = '3'; для первой строки кода (в противном случае вы получите довольно странный ответ). Основной принцип состоит в том, что коды ASCII для цифр являются последовательными, то есть код для «0» = 48, код для «1» = 49 и так далее. Вычитание «0» просто преобразует код ASCII в фактическую цифру, например, «3» - «0» = 3 и т. Д. Обратите внимание, что это будет работать только в том случае, если символ, из которого вычитается '0', является действительной цифрой - в противном случае результат будет иметь небольшое значение.

  2. а. Без контекста «почему» этого кода невозможно сказать. Что касается того, что он делает, то кажется, что оператор if оценивается как true, когда бит i указателя-> intX не установлен, то есть этот конкретный бит равен 0. Я считаю, что оператор & выполняется перед оператором ~, как ~ Оператор имеет очень низкий приоритет. Код мог бы лучше использовать круглые скобки, чтобы сделать намеченный порядок операций более понятным. В этом случае порядок операций может не иметь значения - я считаю, что результат одинаков в любом случае.

    б. Это просто создание числа со всеми битами, КРОМЕ бита i установлен на 1. Удобный способ создать маску для бита i - это использовать выражение (1 << i). </p>

  3. Операция побитового ИЛИ в этом случае используется для установки битов, указанных константой ROW, равными 1. Если эти биты не установлены, она устанавливает их; если они уже установлены, это не имеет никакого эффекта.

0 голосов
/ 24 марта 2009

1) Может кто-нибудь объяснить мне, как работает следующий код? символ а = 3; int x = a - '0'; Я обязуюсь сделать это, чтобы преобразовать символ в int, однако я не понимаю логику, стоящую за ним. Почему / Как это работает?

Конечно. переменная a имеет тип char и, заключив одинарные кавычки в 0, заставляет C также рассматривать его как char. Наконец, весь оператор автоматически типизируется по своему целочисленному эквиваленту, потому что x определяется как целое число.

2) Теперь, что касается битовых операторов, я чувствую себя действительно потерянным здесь. --- Что делает этот код? if (~ pointer-> intX & (1 << i)) {c ++; n = i; } Я где-то читал, что ~ инвертирует биты, но я не вижу, что делает этот оператор и почему он это делает. </strong>

(~pointer->intX & (1 << i)) is saying:

отрицание intX и AND с 1 сдвинутым влево на i битов

Итак, что вы получаете, если intX = 1011 и i = 2, равно

(0100 & 0100) 

-negate 1011 = 0100

-(1 << 2) = 0100

0100 & 0100 = 1 :)

тогда, если операция AND возвращает 1 (что, в моем примере, так и происходит) {c ++; n = i; } * * Тысяча двадцать-один

Итак, увеличьте c на 1 и установите переменную n равной i

То же самое с этой строкой: row.data = ~ (1 << i); </strong>

Same principle here.
Shift a 1 to the left by i places, and negate.

So, if i = 2 again

(1 << 2) = 0100

~(0100) = 1011

** --- Другой вопрос:

if (x != a) { ret |= ROW; }

Что точно делает оператор | =? Из того, что я прочитал, | = ИЛИ, но я не совсем понимаю, что делает это утверждение. **

if (x! = A) (надеюсь, это очевидно для вас .... если переменная x не равна переменной a)

ret |= ROW;

equates to

ret = ret | ROW;

, что означает двоичное ИЛИ ret с ROW

Для примеров того, что именно выполняют операции И и ИЛИ, вы должны хорошо понимать бинарную логику.

Проверьте в википедии таблицы истинности ... т.е.

Битовые операции

0 голосов
/ 24 марта 2009

Уловка вычитания, на которую вы ссылаетесь, работает, потому что числа ASCII расположены в порядке возрастания, начиная с нуля. Таким образом, если ASCII «0» - это значение 48 (и оно есть), то «1» - это значение 49, «2» - это 50 и т. Д. Следовательно, ASCII («1») - ASCII («0») = 49 - 48 = 1.

Что касается побитовых операторов, они позволяют вам выполнять битовые операции с переменными.

Давайте разберем ваш пример:

(1 << i) - это смещение влево константы 1 на i бит. Поэтому, если i = 0, результат является десятичным 1. Если i = 1, он сдвигает первый бит влево, заполняя нули, возвращая двоичный код 0010 или десятичный 2. Если i = 2, вы сдвигаете два бита в слева, заполнение нулями, с двоичным кодом 0100 или десятичным 4 и т. д.

~pointer->intX - это получение значения члена указателя intX и инвертирование его битов, установка всех нулей в единицы и наоборот.

& - оператор амперсанда выполняет побитовое И сравнение. Результатом этого будет 1, где левая и правая части выражения равны 1, и 0 в противном случае.

Таким образом, проверка будет успешной, если pointer->intX имеет нулевой бит в i-й позиции справа.

Кроме того, |= означает выполнение побитового ИЛИ сравнения и присвоение результата левой части выражения. Результат побитового ИЛИ равен 1 для каждого бита, где соответствующий бит левой или правой стороны равен 1,

0 голосов
/ 24 марта 2009

1) Символ - это всего лишь 8-битное целое число. '0' == 48, и все, что это означает.

2) (~ (pointer-> intX) & (1 << i)) определяет, не установлен ли «i-й бит» (справа) в элементе intX любого указателя, на который указывает указатель. ~ Инвертирует биты, так что все 0 становятся 1, и наоборот, затем 1 << i помещает один 1 в желаемое место и объединяет два значения, так что сохраняется только нужный бит, и все вычисляется true, если этот бит был 0 для начала. </p>

3) | побитовый или. Он принимает каждый бит в обоих операндах и выполняет логическое ИЛИ, создавая результат, в котором каждый бит установлен, если у какого-либо операнда был установлен этот бит. 0b11000000 | 0b00000011 == 0b11000011. | = является оператором присваивания, так же, как a + = b означает a = a + b, a | = b означает a = a | b.

Отсутствие побитовых операторов МОЖЕТ упростить чтение в некоторых случаях, но обычно это также значительно замедляет ваш код без сильной оптимизации компилятора.

0 голосов
/ 24 марта 2009

Я думаю, что вы, вероятно, опечатка, и имел в виду:

char a = '3';

Причина, по которой это работает, состоит в том, что все числа располагаются по порядку, и «0» является первым. Очевидно, «0» - «0» = 0. «1» - «0» = 1, поскольку значение символа для «1» на единицу больше значения символа для «0». И т.д.

0 голосов
/ 24 марта 2009

Для char a = 3; int x = a - '0'; Я думаю, что вы имели в виду char a = '3'; int x = a - '0';. Это достаточно легко понять, если вы понимаете, что в ASCII числа располагаются по порядку, например, «0», «1», «2», ... Итак, если «0» равно 48, а «1» равно 49, то «1 '-' 0 'равно 1.

Для побитовых операций их трудно понять, пока вы не начнете смотреть на биты. Когда вы просматриваете эти операции над двоичными числами, вы можете точно видеть, как они работают ...

010 & 111 = 010
010 | 111 = 111
010 ^ 111 = 101
~010 = 101
0 голосов
/ 24 марта 2009
char a = '3';  
int x = a - '0';

у вас была здесь опечатка (обратите внимание на символы вокруг 3), это присваивает значение ascii символа 3 переменной char, затем следующая строка принимает '3' - '0' и присваивает ее x из-за того, как работают значения ascii, x будет равен 3 (целочисленное значение)

В первом сравнении, я никогда не видел, чтобы такой указатель раньше использовался таким образом, может быть, еще одна опечатка? Если бы я зачитал следующий код:

(~pointer->intX & (1 << i))

Я бы сказал "(значение intX разыменовано из указателя) И (1 раз смещено влево i раз)"

1 << i - это быстрый способ умножения 1 на степень 2, т. Е. Если i равно 3, то 1 << 3 == 8 </p>

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

Во 2-м сравнении x | = y совпадает с x = x | у

0 голосов
/ 24 марта 2009

Одинарные кавычки используются, чтобы указать, что используется один символ. «0» - это символ «0», который имеет ASCII-код 48. 3-'0' = 3-48

'1 << i' сдвигает 1 i влево, поэтому только i-й бит справа равен 1. <br> ~ pointer-> intX отменяет поле intX, поэтому логическое И возвращает истинное значение (не 0), когда intX имеет каждый бит, кроме i-го бита справа.

0 голосов
/ 24 марта 2009

ret |= ROW;

эквивалентно

ret = ret | ROW;

...