битовые сдвиги, неподписанные символы

Кто-нибудь может многословно объяснить, что это делает? Я пытаюсь выучить c, и мне трудно оборачиваться вокруг него.

void tonet_short(uint8_t *p, unsigned short s) {
p[0] = (s >> 8) & 0xff;
p[1] = s & 0xff;
}

void tonet_long(uint8_t *p, unsigned long l)
{
p[0] = (l >> 24) & 0xff;
p[1] = (l >> 16) & 0xff;
p[2] = (l >> 8) & 0xff;
p[3] = l & 0xff;
}

3

Решение

Многословно, здесь это идет:

Как прямой ответ; оба они хранят байты переменной внутри массива байтов слева направо. tonet_short делает это для unsigned short переменные, которые состоят из 2 байтов; а также tonet_long делает это для unsigned long переменные, которые состоят из 4 байтов.

Я объясню это для tonet_long, а также tonet_short это будет лишь вариант, который, надеюсь, вы сможете получить сами:

unsigned переменные, когда их биты сдвинуты по битам, сдвигают их биты в сторону определенной стороны для определенного количества битов, а освобожденные биты делаются так, чтобы 0нули. т.е .:

unsigned char asd = 10; //which is 0000 1010 in basis 2
asd <<= 2;              //shifts the bits of asd 2 times towards left
asd;                    //it is now 0010 1000 which is 40 in basis 10

Имейте в виду, что это для unsigned переменные, и они могут быть неверными для signed переменные.

Побитовый и & оператор сравнивает биты двух операндов с обеих сторон, возвращает 1 (правда), если оба 1 (правда) и 0 (ложь), если какой-либо из них или оба 0 (ложный); и это делает это для каждого бита. Пример:

unsigned char asd = 10; //0000 1010
unsigned char qwe = 6;  //0000 0110
asd & qwe;              //0000 0010 <-- this is what it evaluates to, which is 2

Теперь, когда мы знаем побитовое смещение и побитовое и, давайте перейдем к первой строке функции tonet_long:

p[0] = (l >> 24) & 0xff;

Здесь, так как l является unsigned long, (l >> 24) будет оцениваться в первом 4 * 8 - 24 = 8 биты переменной l, который является первым байтом l, Я могу визуализировать процесс следующим образом:

abcd efgh   ijkl mnop   qrst uvwx   yz.. ....   //letters and dots stand for
//unknown zeros and ones
//shift this 24 times towards right
0000 0000   0000 0000   0000 0000   abcd efgh

Обратите внимание, что мы не меняем lэто просто оценка l >> 24, который является временным.

Тогда 0xff который просто 0000 0000 0000 0000 0000 0000 1111 1111 в шестнадцатеричной системе счисления (основание 16), получает побитовое добавление с побитовым сдвигом l, Это выглядит так:

0000 0000   0000 0000   0000 0000   abcd efgh
&
0000 0000   0000 0000   0000 0000   1111 1111
=
0000 0000   0000 0000   0000 0000   abcd efgh

поскольку a & 1 будет просто зависеть строго от aтак будет a; и то же самое для остальных … Это выглядит как избыточная операция для этого, и это действительно так. Это будет, однако, важно для остальных. Это потому, что, например, когда вы оцениваете l >> 16это выглядит так:

0000 0000   0000 0000   abcd efgh   ijkl mnop

Так как мы хотим только ijkl mnop часть, мы должны отказаться от abcd efghи это будет сделано с помощью 0000 0000 тот 0xff имеет соответствующие биты.

Надеюсь, это поможет, остальное происходит так, как далеко, так что … да.

7

Другие решения

Эти подпрограммы преобразуют 16- и 32-битные значения из собственного порядка байтов в стандартный порядок байтов в сети (с прямым порядком байтов). Они работают, сдвигая и маскируя 8-битные чанки из собственного значения и сохраняя их по порядку в байтовом массиве.

1

Если я вижу это правильно, я в основном переключаю порядок байтов в коротком и длинном … (меняет порядок байтов числа) и сохраняю результат по адресу, который, как мы надеемся, имеет достаточно места 🙂

0

explain verbosely — ХОРОШО…

    void tonet_short(uint8_t *p, unsigned short s) {

short обычно это 16-битное значение (максимум: 0xFFFF)

uint8_t является 8-разрядным значением без знака и p это указатель на некоторое количество беззнаковых 8-битных значений (из кода мы предполагаем как минимум 2 последовательных значения).

  p[0] = (s >> 8) & 0xff;

Это берет «верхнюю половину» значения в s и помещает его в первый элемент в массиве p, Итак, давайте предположим, s==0x1234,
Первый s сдвигается на 8 бит (s >> 8 == 0x0012)
тогда это И с 0xFF и результат сохраняется в p[0], (p[0] == 0x12)

  p[1] = s & 0xff;

Теперь обратите внимание, что когда мы делали этот сдвиг, мы никогда не меняли первоначальное значение s, так s по-прежнему имеет первоначальную стоимость 0x1234таким образом, когда мы делаем эту вторую строку, мы просто делаем еще одно побитовое И и p[1] получить «нижнюю половину» значения s (p[0] == 0x34)

То же самое относится и к другой функции, которая у вас есть, но это long вместо короткого, поэтому мы предполагаем, p в этом случае достаточно места для всех 32-битных (4х8), и мы должны сделать несколько дополнительных смен.

0

Этот код используется для сериализации 16-битного или 32-битного числа в байты (uint8_t). Например, записать их на диск или отправить по сетевому соединению.

16-битное значение делится на две части. Один содержит 8 старших значащих (младших) битов, другой содержит 8 младших младших битов. Сначала сохраняется старший байт, затем младший байт. Это называется порядком байтов с прямым порядком байтов или «сетевым». Вот почему функции названы tonet_,

То же самое сделано для четырех байтов 32-битного значения.

& 0xff операции на самом деле бесполезны. Когда 16-битное или 32-битное значение преобразуется в 8-битное значение, младшие 8 бит (0xff) маскируются неявно.

Сдвиги битов используются для перемещения необходимого байта в младшие 8 бит. Рассмотрим биты 32-битного значения:

AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD

Самый значимый байт — 8 названных бит A, Чтобы переместить их в младшие 8 бит, значение должно быть смещено вправо на 24.

0

Имена функций — это большой намек … «на короткую сеть» и «на длинную сеть».

Если вы думаете о десятичной дроби … скажем, у нас есть два клочка бумаги, таких маленьких, что мы можем написать только одну цифру на каждой из них, поэтому мы можем использовать обе для записи всех чисел от 0 до 99: 00, 01, 02. .. 08, 09, 10, 11 … 18, 19, 20 … 98, 99. В основном, один лист бумаги содержит столбец «десятки» (учитывая, что мы в десятичной системе счисления), и другие «единицы».

Память работает так, где каждый байт может хранить число от 0..255, поэтому мы работаем с базой 256. Если у вас есть два байта, один из них будет «двести пятьдесят шесть» столбец, а другой столбец «единицы». Чтобы вычислить объединенное значение, вы умножаете первое на 256 и добавляете второе.

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

Следовательно, некоторые компьютеры хранят 258 — что составляет 1 * 256 + 2 — как низкий = 1 высокий = 2, в то время как другие сохраняют низкий = 2 высокий = 1.

Эти функции выполняют перестановку памяти из того, что использует ваш ЦП, в предсказуемый порядок, а именно: более значимые значения переходят в младшие адреса памяти, и в конечном итоге значение «единиц» помещается в самый высокий адрес памяти. , Это последовательный способ хранения чисел, который работает на всех типах компьютеров, поэтому он прекрасно подходит для передачи данных по сети; если принимающий компьютер использует другой порядок памяти для цифр base-256, он может переместить их из сетевого порядка байтов в любой порядок, который ему нравится, прежде чем интерпретировать их как собственные числа ЦП.

Таким образом, «к чистой короткой» пакеты наиболее значимых 8 бит s в p[0] — нижний адрес памяти. Это на самом деле не нужно & 0xff так как после взятия 16 входных битов и смещения их 8 в «правую», все левые 8 бит в любом случае гарантированно равны 0, что является следствием & 0xFF — например:

      1010 1111 1011 0111  // = decimal 10*256^3 + 15*256^2 + 11*256 + 7
>>8   0000 0000 1010 1111 // move right 8, with left-hand values becoming 0
0xff 0000 0000 1111 1111 // we're going to and the above with this
&    0000 0000 1010 1111 // the bits that were on in both the above 2 values
// (the and never changes the value)
0