有没有办法创建只能具有 3 个值之一的变量?
我想创建一个返回三种可能状态之一的函数,我认为在这种情况下使用整数会不必要地占用大量内存空间。
那么,有什么方法可以创建仅在内存中存储 3 位并且可以用作函数返回的变量?如果是这样,请给我建议如何将其重写为更好的东西:
int ReturnOneOfThreeStates(){
return 0 //Let's say 0=green, 1=red, 2=blue
}
有没有办法创建只能具有 3 个值之一的变量?
您可以使用枚举:
enum TriState { One, Two, Three };
在 C++11 中,您可以指定表示枚举的基础整数类型:
enum TriState : char { One, Two, Three};
C++11 的另一个优点是您可以将其设为强类型:
enum class TriState : char { One, Two, Three};
唯一真正的解决方案是创建一个子范围类,它将值存储在一个int
(或其他整数类型)上,并在每个可能修改值的函数中强制执行范围不变量。
请注意,空间参数仅适用于该类型的大型数组。在这种情况下,可以编写一个MyValueTypeVector
类,每个值只使用 2 位(如果最多有三个值)。在大数组之外,提取和插入值所需的额外代码将占用比值本身更多的空间;在许多情况下,编译器会将int
a 放入寄存器中,它根本不会占用任何空间。并且不要忘记,即使在大型数组中,您也可能会将访问时间(从索引)增加一个数量级。
答案分为两部分:您需要一种返回三个值之一的方法,以及一种将多个此类值打包到内存中的方法。
枚举类型的值可以解决第一个问题:通过定义一个,enum
您可以将函数返回的值的声明范围限制为三个值。但是,这不会让您节省任何内存:enum
s 不能小于 8 位。
您需要将较小的值打包成较大的值以节省内存。这仅在您声明足够大小的值数组以使打包有意义时才有效。基本方法是切出您需要的位,将它们移动到适当的位置,然后OR
移到所需的位置。
这是使用 2 位数字的方法:一个 8 位值可以存储四个这样的数字。N
2 位值的数组需要(N+3)/4
8 位值。位置处的元素在x
字节x/4
中,在位置中x%4
(即0
、1
、2
或3
)。您将获得所需的 2 位元素,如下所示:
int twoBit = (array[x/4] >> (2*(x%4))) & 3;
// ^^^^^^^^^^ ^ ^^^ ^
// | | | +-- Get the last two bits
// | | +---------- Get sub-element 0..3
// | +-------------- Multiply by 2, because there are 2 bits per subelement
// +------------------------ Get the desired 8-bit element
设置值则相反:
uint8_t mask = 3 << (2*(x%4)); // Prepare the mask
array[x/4] &= ~mask; // Clear out the desired two bits
array[x/4] |= (twoBit & 3) << (2*(x%4)); // OR in the desired bits
如果您不需要数组,但想在一个较大的struct
或 a中打包几个两位值class
,您可以使用Bit Fields。在这种情况下,如果可以打包数据,编译器将为您完成所有打包工作。请注意,您放置数据成员的顺序很重要,并且可以更改您节省的内存量。
是和不是。如果它是另一个类或结构的一部分,那么是的,您可以使用位域获得有效的好处。
// bit_fields1.cpp
struct Date
{
unsigned nWeekDay : 3; // 0..7 (3 bits)
unsigned nMonthDay : 6; // 0..31 (6 bits)
unsigned nMonth : 5; // 0..12 (5 bits)
unsigned nYear : 8; // 0..100 (8 bits)
};
你可以返回一个只有一个成员位域的结构(例如,只是上面的一个字段)---但不确定它是否能解决你的情况。函数的返回值至少消耗一个 8 位寄存器想象一下你关心的任何目标机器......
这里有两个问题。
一个是关于函数参数和返回值等,它们只包含你正在谈论的值。这些不可能少于一个字符。实际上,让它们占用比整数更少的空间在大多数情况下并不是非常有用。
另一个是关于存储。要么存储为类的成员,要么存储这些值的某种集合。在那里,您可以使用各种技术和技巧让它们占用不到一个字节。
事实上,如果你有大量的它们,或者需要将它们与许多其他类型的值范围非常有限的数据结构结合起来存储,你甚至可以让它们占据一小部分。这将涉及 CPU 权衡,以从它们共享空间的较大数据类型中提取值。但这是可能的。
就目前而言,如果您总是将这个小范围值与相同组合中的其他值传递,您可以将函数参数问题变成存储问题。但如果它们需要直接作为参数传递,则不能这样做。
我将很快发布几个示例,说明如何为每种用途定义这种类型。
对于价值传递,这个答案有它。使用enum
. 在 C++11 中,您可以选择enum
将 表示为使用enum Foo : char { Val1, Val2, Val3 };
.
要存储它们的整个数组,您可以使用以下技术:
#include <limits>
enum Foo : char { Val1, Val2, Val3 };
template <unsigned int NumFoos>
class FooArray {
static constexpr unsigned int foos_in_x(unsigned long long x) noexcept(true) {
return (x > 3) ? (1 + foos_in_x(x / 3)) : 0;
}
static constexpr unsigned int foos_per_ll = foos_in_x(::std::numeric_limits<unsigned long long>::max());
static constexpr unsigned long long array_size() noexcept(true) {
return (NumFoos + (foos_per_ll - 1)) / foos_per_ll;
}
static constexpr unsigned int div_value(unsigned int n) {
return (n == 0) ? 1 : (3 * div_value(n - 1));
}
public:
explicit FooArray(Foo defaultval = Val1);
~FooArray();
Foo operator[](unsigned long long pos) const {
const unsigned long long arypos = pos / foos_per_ll;
const unsigned int fooval = (foovals_[arypos] / div_value(pos % foos_per_ll)) % 3;
return (fooval == 0) ? Val1 : ((fooval == 1) ? Val2 : Val3);
}
private:
unsigned long long foovals_[array_size()];
};
当然,非常operator []
量将不得不返回一个特殊的引用类型,如果它被分配到,它将在数组中调整适当的值。如果您发现如何做到这一点令人困惑,我可以写一个分配函数如何工作的示例。这类似于如何从其 non-const::std::bitset
返回类型。reference
operator []