#c #compiler-errors #bit-manipulation #c 17 #constexpr
#c #ошибки компилятора #манипулирование битами #c 17 #constexpr
Вопрос:
Я хочу написать функцию, оцениваемую во время компиляции, она принимает указатель на массив из 4 байт и выводит int, который имеет тот же битовый шаблон, что и этот массив. Итак, я придумал:
constexpr int f(const char* p) {
return *reinterpret_cast<int*>(p);
}
Затем я хочу использовать f()
так:
switch(x) {
case f("GOOG"):
// do something
case f("MSFT"):
// do something
case f("NIKE"):
// do something
}
Однако я получил ошибку компилятора:
ошибка: доступ к значению ‘"GOOG"’ через ‘int’ glvalue в случае константного выражения f("GOOG")
- Как исправить
f()
, чтобы он компилировался? - Есть ли лучший способ достичь той же цели?
Комментарии:
1. Даже если он будет скомпилирован, это будет строгим нарушением псевдонимов и UB. Используйте битовые сдвиги, чтобы получить целое число из отдельных
char
s.2. @HolyBlackCat Спасибо. кстати, что такое UB?
3. UB — это неопределенное поведение.
4. Спасибо. Я чувствую, что выполнение сдвига битов 4 раза не так элегантно, как просто рассматривать его как int . Нет ли способа сделать это безопасно?
5. Вы также можете создать
int
иmemcpy
массив в нем, ноmemcpy
это неconstexpr
так.
Ответ №1:
Поздравляем, вы активировали карточку ловушки строгого сглаживания, и ваш код имеет неопределенное поведение (если он будет компилироваться).
В вашем коде мало ошибок, «правильная» версия:
constexpr int f(const char* p) {
return *reinterpret_cast<const int*>(p);
}
reinterpret_cast
невозможно отброситьconst
.cursor->p
опечатка?
Но поскольку const char*
не указывает на an int
, приведение к нему нарушает строгое правило псевдонимов. int
не является одним из типов, которые могут использовать псевдонимы других — только std::byte, (unsigned) char
can .
Самым чистым было бы это:
#include <cstring>
constexpr int f(const char* p) {
int val = 0;
static_assert(sizeof(val)==4); // If the array is 4-byte long.
std::memcpy(amp;val,p,sizeof val);
return val;
}
Но std::memcpy
это не constexpr
так, даже во время выполнения это, вероятно, не будет иметь никаких накладных расходов, компилятор может распознать это и переинтерпретировать байты самостоятельно.
Так что переходите к битовому сдвигу:
constexpr int f(const char* p) {
int value=0;
using T = decltype (value);
for(std::size_t i =0; i< sizeof(T); i)
value|= (T)p[i] << (8*i);
return value;
}
int main(){
// @ == 64
// 1077952576 = 01000000 01000000 01000000 01000000
static_assert(f("@@@@") ==1077952576);
}
Просто чтобы быть педантичным "@@@@"
, имеет длину 5, а не 4.
Комментарии:
1. курсор был опечатан. исправлено. TY.
2. Можете ли вы объяснить, что это
value|= (T)*p << (8*i);
делает? Похоже, что он использует только первый символ p, сдвигается влево на 1 байт и выполняет побитовое или со значением. т. е. использует ли он остальные символы? p[1], p[2] и т.д.3. @ijklr Ой, извините, конечно, в нем отсутствует индекс, глупо, что я сделал слишком простой тестовый пример. Исправлено.
4. Спасибо. и причина, по которой вы использовали
(T)
вместоreinterpret_cast<T>
, заключается в том, что внутриconstexpr
функций вы не можете переинтерпретировать, верно?5. Жаль, что я не могу просто преобразовать его в int и не нужно выполнять цикл:(