指向枚举数组的引用/指针未获得正确的值

Reference/pointer to array of enums not getting the right valu

提问人:Arni 提问时间:9/8/2023 最后编辑:Arni 更新时间:10/5/2023 访问量:76

问:

我有一个枚举数组:

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++ 数组 指针 枚举 强制转换

评论

1赞 463035818_is_not_an_ai 9/8/2023
请发布实际代码,一个最小的可重现示例。在您发布的枚举中,没有Item2
1赞 463035818_is_not_an_ai 9/8/2023
你为什么要投到?具有 3 个枚举器的枚举的基础类型不一定是 .我不懂 C,但总的来说,有效的 C 代码并不总是有效的 C++ 也就不足为奇了。它们是两种不同的语言int32_t*int32_t
1赞 463035818_is_not_an_ai 9/8/2023
你发布的代码有太多不清楚的东西(缺少枚举项,在代码中你说在 C 中会很好,缺少甚至更多)。没有什么是显而易见的,因此您在代码中引入的任何与您的实际代码无关的小错误都会使我们无法知道您的实际代码是什么样子的。请发布一个最小的可重现示例static_cast;
5赞 pptaszni 9/8/2023
int32_t valueToFind;这是未初始化的,因此是未定义的行为。
2赞 pptaszni 9/8/2023
“int32_t valueToFind;只是一个例子“——SO 中没有”只是一个例子,我的代码不同“的地方。如果您准确地显示了导致问题的代码,那么就有可能有人提供帮助。否则只是猜测。

答:

3赞 pptaszni 9/8/2023 #1

c++17 标准草案 [dcl.enum] 10.2.7.

对于基础类型不固定的枚举,基础类型是可以 表示枚举中定义的所有枚举器值。如果没有整型可以表示所有 枚举器值,则枚举格式不正确。它是实现定义的,使用哪种整型 作为基础类型,但基础类型不得大于 int,除非 枚举器不能容纳 int 或无符号 int。如果枚举器列表为空,则基础类型为 如果枚举具有值为 0 的单个枚举器

因此,您通过对可能不同类型的指针进行转换和取消引用来导致未定义的行为,例如 房 协~。例如,gcc 13 使用无符号整数作为枚举的基础类型。std::int32_tstd::uint32_t

只是不要在需要整数的地方使用枚举(如for循环)。例如,您可以传递给您的函数,并使用而不是文字整数 0 进行查找。std::vector<ITEMS_TYPE>Item0

评论

1赞 463035818_is_not_an_ai 9/8/2023
“干脆不要在需要整数算术的上下文中使用枚举。例如,您可以传递 [枚举向量] ...“ ?
0赞 pptaszni 9/8/2023
@463035818_is_not_an_ai喜欢......或者别的什么。我见过很多项目,有人从为所有东西定义枚举开始,然后以这样的问题结束。如何遍历枚举。如何使用一些偏移量开始迭代。如何从集合中排除某些枚举。我可以添加/减去两个枚举吗?如果它们以“what/why”而不是“how”==枚举开头,所有这些问题都不会出现。for(int i = Item0; i < ItemEnd;
1赞 463035818_is_not_an_ai 9/8/2023
好吧,这就是我思考了一会儿后对这一段的理解,我只是觉得你的意思不是很清楚;)
0赞 Oersted 9/8/2023 #2

请仔细阅读以上所有有价值的评论。

这是一种可能的 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(),以避免名称冲突。
用于数组索引。在此代码中,我添加了一个可能的函数,用于自动推断数组大小 ()。
正确初始化变量。在这里,我给(这里是最大值)一个特殊的值。如果测试函数输出,则不是必需的,但至少,如果搜索失败,您将获得一个定义明确的值。
设置严格的编译选项,并注意编译器返回的所有诊断。
我希望它会有所帮助。
注意:还有很多其他的事情可以说,但我觉得这超出了你的问题的范围。
enumenumenumenum classstd::size_tSizeindexintenum

实时提供“严重”编译选项

[编辑]记录一个更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++(或),但我对它们不够熟悉。spanrange

[编辑] 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)

评论

0赞 463035818_is_not_an_ai 9/8/2023
您可以将指针传递给第一个元素,并将指针传递到最后一个元素之外,或者通过引用传递数组,或者使用 开始。恕我直言,任何事情都比将指针分别传递到第一个元素和大小要好。std::spanstd::array
0赞 Oersted 9/8/2023
当然,有更好的方法来管理值数组,但对我来说这似乎超出了范围(否则可能已经去了),你不这么认为吗?恕我直言,OP 有趣的是其问题背后的原因是什么。std::find
0赞 463035818_is_not_an_ai 9/8/2023
恕我直言,当代码是关于在数组中查找元素时,既不正确使用数组,也不“超出范围”。坦率地说,你提到了很多东西,但 OPs 代码中的实际问题是什么,读完这个答案后我不知道。std::find
0赞 463035818_is_not_an_ai 9/8/2023
你错过了解释为什么演员是错的int32_t*
0赞 Arni 9/8/2023
1.这太好了,它有效。2. 是的,我想看看你如何自动计算arrayLastIndex。我的方法是将 ItemLast 添加到枚举类ITEMS_TYPE。3. 我使用枚举作为数字的别名,因为我从事的项目部分是用 C 编写的,部分是用 C++ 编写的。我在 C 中不是那么好,在 C++ 中也很糟糕。4. 我最初问题的解决方案是使用 size_t 对 findIndexOfItemInArray() 中的数组进行索引。我要特别感谢你的建议。