Хорошо, я чувствую себя глупо, спрашивая это, но почему код ниже выводит разные строки?
Чтобы напечатать первую строку, я беру адрес к первому байту массива, интерпретирую его как указатель на uint16_t, беру значение и печатаю его биты один за другим.
Для второй строки я беру указатель на первый байт, интерпретирую его как указатель на uint8_t, беру значение и печатаю его биты один за другим. Затем сделайте то же самое со вторым байтом.
Поскольку я не изменяю память, выделенную для массива, только интерпретирую ее по-разному, я ожидаю, что выходные данные будут одинаковыми, но порядок байтов различен.
Я, вероятно, что-то упускаю, но единственное, что я думаю, это то, что оператор косвенного обращения делает то, чего я не ожидаю.
#include <iostream>
#include <string.h>int main() {
uint8_t u[2];
u[0] = 170;
u[1] = 85;
for(int i = 15; i >= 0; --i) {
printf( "%u", (((*((uint16_t*)u)) >> i) & 0x0001));
}
printf( "\n");
for(int i = 7; i >= 0; --i) {
printf( "%u", (((*((uint8_t*)u)) >> i) & 0x01));
}
for(int i = 7; i >= 0; --i) {
printf( "%u", (((*((uint8_t*)(u + 1))) >> i) & 0x01));
}
}
Outout
0101010110101010
1010101001010101
Обновление № 1: пожалуйста, игнорируйте распределение, да пример кода не работает на каждой ОС, но это просто упрощенный пример.
Обновление № 2: я знал о порядке байтов, но то, что я пропустил, это логическое и физическое представление битов. В приведенном выше примере, даже если физическое представление не изменилось, я печатаю логическое представление, на которое влияет порядок байтов. Большое спасибо @ john-kugelman за объяснение этого.
На платформах Intel числа хранятся в порядок байтов. Наименее значимый байт — первый, самый старший — последний. Это противоположно тому, как мы обычно читаем числа. Если бы мы писали числа с прямым порядком байтов, а не с прямым порядком байтов, была бы написана тысяча двадцать три 3201
вместо 1023
,
Когда вы интерпретируете байты в массиве байтов как 16-разрядное целое число, первый байт (170) интерпретируется как наименее значимый байт, а второй байт (85) является наиболее значимым. Но когда вы печатаете байты самостоятельно, вы печатаете их в обратном порядке. Вот откуда исходит несоответствие.
Порядок байтов это свойство для конкретной платформы. Большинство архитектур не-Intel используют более «естественный» порядок байтов с прямым порядком байтов. К сожалению для нас, архитектуры на базе Intel являются наиболее распространенными. Как это бывает, почти весь сетевой трафик имеет порядок байтов, также известный как «порядок байтов в сети». Когда машины на базе Intel общаются в интернете, они выполняют значительную перестановку байтов при отправке и получении данных.
Я ожидал, что это несоответствие произойдет, если я распечатаю сам uint16_t. Я не понимаю, почему это происходит, когда я пытаюсь понять его.
Чтение битов с помощью операций маскирования и сдвига битов не считывает физическое биты в памяти слева направо, он читает логический биты от старшего к младшему. На архитектуре с прямым порядком байтов значение «от младшего к младшему» соответствует порядку справа налево.
Также обратите внимание, что порядковый номер означает байтов поменялись местами, а не биты. Биты не переставляются в младшей архитектуре, байты. Биты нельзя поменять местами, потому что они не адресованы индивидуально. На них можно попасть только с помощью смен и масок.
Два байта в памяти:
0xAA
0x55
Когда они интерпретируются как 16-битное слово, возможны два значения.
На основе порядка байтов процессора:
Little Endian (младший байт первый): 0x55AA // Intel x86 / x64
Big Endian (самый старший байт первый): 0xAA55 // Power, ARM и т. Д.
Возможно, ошибка aligment и отсутствующие модификаторы длины строки формата оставлены в стороне, у вас есть проблема с порядком байтов. Этот термин описывает, как типы данных, длина которых превышает наименьшую адресуемую единицу (т.е. байт), хранятся в памяти.
Похоже, ваша система использует младший порядок для 16-битных целых чисел: младший байт хранится по младшему адресу.
Обратите внимание, что нет причин приводить к последним двум циклам for, так как вы используете тот же тип, что и элементы массива. Никогда не разыгрывайте без веской причины и всегда пытайтесь кодировать то, что вам не нужно разыгрывать. Приведение не позволяет компилятору помочь вам обнаружить несоответствие типов. Таким образом, вы применяете только в том случае, если вы абсолютно уверены, что знаете лучше, чем компилятор, что вы делаете.