Кто-нибудь может многословно объяснить, что это делает? Я пытаюсь выучить 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;
}
Многословно, здесь это идет:
Как прямой ответ; оба они хранят байты переменной внутри массива байтов слева направо. 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
имеет соответствующие биты.
Надеюсь, это поможет, остальное происходит так, как далеко, так что … да.
Эти подпрограммы преобразуют 16- и 32-битные значения из собственного порядка байтов в стандартный порядок байтов в сети (с прямым порядком байтов). Они работают, сдвигая и маскируя 8-битные чанки из собственного значения и сохраняя их по порядку в байтовом массиве.
Если я вижу это правильно, я в основном переключаю порядок байтов в коротком и длинном … (меняет порядок байтов числа) и сохраняю результат по адресу, который, как мы надеемся, имеет достаточно места 🙂
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), и мы должны сделать несколько дополнительных смен.
Этот код используется для сериализации 16-битного или 32-битного числа в байты (uint8_t
). Например, записать их на диск или отправить по сетевому соединению.
16-битное значение делится на две части. Один содержит 8 старших значащих (младших) битов, другой содержит 8 младших младших битов. Сначала сохраняется старший байт, затем младший байт. Это называется порядком байтов с прямым порядком байтов или «сетевым». Вот почему функции названы tonet_
,
То же самое сделано для четырех байтов 32-битного значения.
& 0xff
операции на самом деле бесполезны. Когда 16-битное или 32-битное значение преобразуется в 8-битное значение, младшие 8 бит (0xff
) маскируются неявно.
Сдвиги битов используются для перемещения необходимого байта в младшие 8 бит. Рассмотрим биты 32-битного значения:
AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD
Самый значимый байт — 8 названных бит A
, Чтобы переместить их в младшие 8 бит, значение должно быть смещено вправо на 24.
Имена функций — это большой намек … «на короткую сеть» и «на длинную сеть».
Если вы думаете о десятичной дроби … скажем, у нас есть два клочка бумаги, таких маленьких, что мы можем написать только одну цифру на каждой из них, поэтому мы можем использовать обе для записи всех чисел от 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)