提问人:Arni 提问时间:9/8/2023 最后编辑:Arni 更新时间:10/5/2023 访问量:76
指向枚举数组的引用/指针未获得正确的值
Reference/pointer to array of enums not getting the right valu
问:
我有一个枚举数组:
typedef enum {
Item0,
Item1,
Item2
} ITEMS_TYPE;
ITEMS_TYPE MyItemsArray[] = {
Item0,
Item1,
Item2
};
bool findIndexOfItemInArray(int32_t valueToFind, int32_t* arrayToScan, int32_t arrayLastIndex, int32_t* indexOfTheSearchedValue)
{
int32_t index;
int32_t value;
for(index = 0; index < arrayLastIndex; index++)
{
//value = static_cast<int32_t>(arrayToScan[index]); //this when C++
value = (int32_t)(arrayToScan[index]); //this when C
if(value == valueToFind)
{
*indexOfTheSearchedValue = index;
return true;
}
}
return false;
}
int main(void)
{
int32_t valueToFind = 0;
int32_t itemIndex;
findIndexOfItemInArray((int32_t)valueToFind, (int32_t*)MyItemsArray, Item2, &itemIndex));
}
我的问题是
arrayToScan[index];
函数内部从来都不是给定项的整数值,而是一些奇怪的长数字。findIndexOfItemInArray()
当我用 C 编译它时,这一切都有效,但在 C++ 中却不行。我错过了什么?
答:
从 c++17 标准草案 [dcl.enum] 10.2.7.
对于基础类型不固定的枚举,基础类型是可以 表示枚举中定义的所有枚举器值。如果没有整型可以表示所有 枚举器值,则枚举格式不正确。它是实现定义的,使用哪种整型 作为基础类型,但基础类型不得大于 int,除非 枚举器不能容纳 int 或无符号 int。如果枚举器列表为空,则基础类型为 如果枚举具有值为 0 的单个枚举器
因此,您通过对可能不同类型的指针进行转换和取消引用来导致未定义的行为,例如 房 协~。例如,gcc 13 使用无符号整数作为枚举的基础类型。std::int32_t
std::uint32_t
只是不要在需要整数的地方使用枚举(如for循环)。例如,您可以传递给您的函数,并使用而不是文字整数 0 进行查找。std::vector<ITEMS_TYPE>
Item0
评论
for(int i = Item0; i < ItemEnd;
请仔细阅读以上所有有价值的评论。
这是一种可能的 C++ 方法。
#include <cstddef>
#include <limits>
enum class ITEMS_TYPE { Item0, Item1, Item2 };
ITEMS_TYPE MyItemsArray[] = {ITEMS_TYPE::Item0, ITEMS_TYPE::Item1,
ITEMS_TYPE::Item2};
// computing fixed-size plain array size at compile-time
template <typename T, std::size_t N>
constexpr std::size_t Size(T const (&)[N]) {
return N;
}
bool findIndexOfItemInArray(ITEMS_TYPE valueToFind, ITEMS_TYPE* arrayToScan,
std::size_t arrayLastIndex,
std::size_t* indexOfTheSearchedValue) {
std::size_t index;
ITEMS_TYPE value;
for (index = 0; index < arrayLastIndex; index++) {
value = arrayToScan[index];
if (value == valueToFind) {
*indexOfTheSearchedValue = index;
return true;
}
}
return false;
}
int main(void) {
ITEMS_TYPE valueToFind = ITEMS_TYPE::Item2;
std::size_t itemIndex =
static_cast<std::size_t>(std::numeric_limits<int>::max());
findIndexOfItemInArray(valueToFind, MyItemsArray, Size(MyItemsArray),
&itemIndex);
return static_cast<int>(itemIndex);
}
首先,我会避免依赖项目的实际数值。它通常违背了使用整体的目的。
然后,我建议使用scoped(),以避免名称冲突。
用于数组索引。在此代码中,我添加了一个可能的函数,用于自动推断数组大小 ()。
正确初始化变量。在这里,我给(这里是最大值)一个特殊的值。如果测试函数输出,则不是必需的,但至少,如果搜索失败,您将获得一个定义明确的值。
设置严格的编译选项,并注意编译器返回的所有诊断。
我希望它会有所帮助。
注意:还有很多其他的事情可以说,但我觉得这超出了你的问题的范围。enum
enum
enum
enum class
std::size_t
Size
index
int
enum
[编辑]记录一个更C++的版本:
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <limits>
enum class ITEMS_TYPE { Item0, Item1, Item2 };
// ITEMS_TYPE MyItemsArray[] = {ITEMS_TYPE::Item0, ITEMS_TYPE::Item1,
// ITEMS_TYPE::Item2};
// C++14
std::array<ITEMS_TYPE, 3> MyItemsArray = {ITEMS_TYPE::Item0, ITEMS_TYPE::Item1,
ITEMS_TYPE::Item2};
// C++17
// std::array MyItemsArray = {ITEMS_TYPE::Item0, ITEMS_TYPE::Item1,
// ITEMS_TYPE::Item2};
int main(void) {
ITEMS_TYPE valueToFind = ITEMS_TYPE::Item2;
std::size_t itemIndex =
static_cast<std::size_t>(std::numeric_limits<int>::max());
auto first = std::cbegin(MyItemsArray);
auto last = std::cend(MyItemsArray);
auto res = std::find(first, last, valueToFind);
if (res != last) {
itemIndex = static_cast<std::size_t>(std::distance(first, res));
}
return static_cast<int>(itemIndex);
}
也可以使用更现代的 C++(或),但我对它们不够熟悉。span
range
[编辑] OP代码的批评者
上面的解释是告诉应该做什么,但并不清楚为什么 OP 代码在低级上也是错误的。
我将重复这个答案,不能假设基础类型是:
来自 c++17 std draft [dcl.enum]
10.2.7.uint32_t
对于基础类型不固定的枚举,基础类型是可以 表示枚举中定义的所有枚举器值。如果没有整型可以表示所有 枚举器值,则枚举格式不正确。它是实现定义的,使用哪种整型 作为基础类型,但基础类型不得大于 int,除非 枚举器不能容纳 int 或无符号 int。如果枚举器列表为空,则基础类型为 如果枚举具有值为 0 的单个枚举器
因此,枚举数组的强制转换是未定义的行为。
相反,可以使用,但是,正如本答案开头所说和解释的那样,我认为这种用法是一种不好的做法。uint32_t *
reinterpret_cast<std::underlying_type_t<ITEMS_TYPE>*>(MyItemsArray)
评论
std::span
std::array
std::find
std::find
int32_t*
评论
Item2
int32_t*
int32_t
static_cast
;
int32_t valueToFind;
这是未初始化的,因此是未定义的行为。