манипулировать типом u64 в Linux

#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 тип.