Почему побитовый оператор XOR C/C++ заботится о знаке?

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

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

Почему побитовый оператор XOR заботится о знаке? Я думал, что это работает на битовом уровне, независимо от того, что они представляют. Вот фрагмент кода, который я использовал, чтобы попытаться понять его.

#include <stdio.h>
#include <stdint.h>
#include <iostream>
#include <bitset>

int main ()
{    
    uint8_t a = 0b10010101;
    char b    = 0b10010101;

    uint32_t checksum;

    checksum = 55;
    checksum ^= a;
    std::cout << std::bitset<32>(checksum) << std::endl;

    checksum = 55;
    checksum ^= b;
    std::cout << std::bitset<32>(checksum) << std::endl;    
}

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


person José Tomás Tocino    schedule 17.09.2018    source источник
comment
Это cstdio в C++, а не stdio.h   -  person KamilCuk    schedule 17.09.2018
comment
Я не вижу здесь никакой операции. Все, что я вижу, это XOR   -  person bartop    schedule 17.09.2018
comment
ИЛИ есть |. ^ — это исключающее ИЛИ   -  person Swift - Friday Pie    schedule 17.09.2018
comment
Вы правы насчет ИЛИ/исключающего ИЛИ.   -  person José Tomás Tocino    schedule 17.09.2018


Ответы (1)


Прежде всего, вы должны помнить, что это определяется реализацией, если char подписано или не подписано.

Затем вы должны помнить, что значения меньших типов, таких как char или uint8_t, продвигаются до int (или, возможно, unsigned int) при использовании в выражениях. Это продвижение включает в себя расширение знака подписанных типов.

Таким образом, если char подписано, значение 0b10010101 будет повышено до 0b11111111111111111111111110010101 (используя дополнение до двух для отрицательные числа). Что сильно отличается от беззнакового значения 0b00000000000000000000000010010101.

person Some programmer dude    schedule 17.09.2018
comment
Несмотря на то, что интегральные преобразования существуют, совершенно очевидно, что оператор на месте, такой как ^=, не может продвигать свой левый операнд. К бинарным операторам применяются обычные арифметические преобразования: a = b ^c;. Неправильно (неверное обобщение) говорить, что повышение происходит с помощью выражений, потому что a^=b также является выражением. - person MSalters; 17.09.2018
comment
но a и b тоже являются выражениями... и оператор не может быть реализован для работы на месте с операндами другого типа. это больше эквивалентно uint32_t operator^=(int32_t a, int32_t b) { uint32_t c = a ^ b; return c; } с фактическими операндами char и uint8_t. Я думаю, это просто вопрос перспективы - person Swift - Friday Pie; 17.09.2018