#c #linux #unix #64-bit
#c #linux #unix #64-разрядный
Вопрос:
Как мы можем манипулировать 32-битным старшим порядком и 32-битным младшим порядком типа u64 в ядре Linux. Я попробовал это, но компилятор выдал множество предупреждений.
#define HI_BYTES(_a) (_a amp; 0xffffffff00000000)
#define LO_BYTES(_a) (_a amp; 0x00000000ffffffff)
/* _v is 32 bit value */
#define HI_BYTES_SET(_a, _v) do {_a = (_a amp; 0x00000000ffffffff) | (_v << 32)} while (0)
#define LO_BYTES_SET(_a, _v) do {_a = (_a amp; 0xffffffff00000000) | (_v)} while (0)
Приветствуются любые предложения. Большое спасибо!
Комментарии:
1. u64? Возможно, вы имеете в виду uint64_t? И вам действительно нужно возиться с макросами вместо того, чтобы зацикливаться на небольшой встроенной функции и позволять компилятору выполнять свою работу?
2. он жаловался на величину сдвига, а постоянное значение 0xffffffff00000000 слишком велико. Спасибо всем, что нашли время ответить
Ответ №1:
Я подозреваю, для начала, что вам понадобятся квалификаторы для этих больших шестнадцатеричных чисел, что-то вроде:
0xffffffff00000000ULL
Целочисленные константы без прикрас относятся к int
типу, и этого, вероятно, недостаточно для хранения заданных значений.
Кроме этого, вы должны публиковать предупреждения, которые вы получаете, чтобы нам не пришлось играть в игру с психической отладкой 🙂
И еще одна вещь, которая может быть проблематичной, — это v << 32
для 32-разрядной v
версии. Я бы, вероятно, выбрал что-то вроде:
#define HI_BYTES(_a) (_a amp; 0xffffffff00000000ULL)
#define LO_BYTES(_a) (_a amp; 0x00000000ffffffffULL)
#define HI_BYTES_SET(_a, _v)
do {
u64 _xyzzy = _v;
_a = (_xyzzy << 32) | LO_BYTES(_a)
} while (0)
#define LO_BYTES_SET(_a, _v)
do {
u64 _xyzzy = _v;
_a = HI_BYTES(_a) | (_xyzzy)
} while (0)
Это позволило бы убедиться, что все имеет правильный тип, прежде чем выполнять какое-либо смещение битов. Имейте в виду, что это непроверено, вам придется подтвердить правильное поведение.
Но, конечно, я пропустил самое очевидное решение, предложенное Николасом Найтом в комментарии. Полностью избавьтесь от макросов. С этим гораздо лучше справляются функции (помеченные как встроенные, если хотите, но я редко нахожу это необходимым, поскольку gcc
в любом случае оптимизирует вещи безумно хорошо).
Таким образом, компилятор может принудительно вводить типы данных, и вы не столкнетесь с проблемами, которые неизменно возникают при использовании макросов типа #define SQR(x) ((x)*(x))
и i = SQR(j )
.
Ответ №2:
Как уже говорилось другими, правильный typedef является uint64_t
. Константы этого типа могут быть получены с помощью предопределенного макроса UINT64_C
, например, UINT64_C(1)
, что, вероятно, приведет к 1ULL
. Но на самом деле я не думаю, что вам это нужно.
Если у вас действительно есть переменная такого типа (т. Е. с фиксированной шириной 64 и без знака), сдвиг дважды на 32 бита всегда должен давать правильный результат. Чтобы иметь только биты старшего порядка
((a >> 32) << 32)
Компилятор оптимизирует это до идеального ассемблера для вашей платформы. (Для gcc скомпилируйте с помощью -march=native
и проверьте ассемблер с помощью -S
.)
Быть уверенным, что это действительно uint64_t
лучшее, действительно, как говорили другие, имеет функцию. Затем это преобразуется в правильные типы, и вам не нужно беспокоиться.
Такое определение небольшой функции принадлежит заголовочному файлу, поэтому вы должны либо объявить его inline
(или static
, если это необходимо), в противном случае вы столкнетесь с ошибками «умножить определенный символ» во время соединения.
Чтобы где-то также был объявлен символ вашей функции, вам пришлось бы поместить extern inline
объявление (не определение) ровно в один модуль компиляции.
Комментарии:
1.
u64
на самом деле является допустимым типом для ядра Linux — не все компиляторы могут предоставлятьuint64_t
тип.