【问题标题】:Iterate through a struct with a pointer使用指针遍历结构
【发布时间】:2021-03-19 16:14:35
【问题描述】:

我目前有这个代码:

struct LAYOUT {
            WORD a [8] = {::Rook, ::Knight, ::Bishop, ::Queen, ::King, ::Bishop, ::Knight, ::Rook};
            WORD b [8] = {::Pawn};
            WORD empty [32] = {::None};
            WORD c [8] = {::Pawn+0x6};
            WORD d [8] = {::Rook+0x6, ::Knight+0x6, ::Bishop+0x6, ::Queen+0x6, ::King+0x6, ::Bishop+0x6, ::Knight+0x6, ::Rook+0x6};
        }chessLayout;
LAYOUT* chessboard = &chessLayout;

::Rook等全局enum字段代表一个词,例如:0x2656

我这里的目标是枚举这个结构中的所有元素,所以我写了这段代码:

for(int i = 0; sizeof(LAYOUT) / 2;i++){
     printf("%x", *(reinterpret_cast<WORD*>(chessboard+i)));
}

但是,这会返回第一个值,但随后会返回不相关的垃圾值,而不是结构中的元素。

有什么想法吗?

提前致谢

【问题讨论】:

  • chessboard+i,你的棋盘指针还是LAYOUT*,对吧?您正在增加错误的大小。我会试试(reinterpret_cast&lt;WORD*&gt;(chessboard)+i)
  • 即使你解决了这个问题,一旦你以这种方式访问​​b[0],你将拥有 UB。因为指针算法有特定的规则。
  • @Jarod42 我知道在数组外迭代是 UB,但即使指针在有效数组内(即使它不是“原始”数组)也是如此吗?
  • 指针运算只能在数组内部进行(+ end)。我们扩展该规则,即常规变量是大小为 1 的数组。所以a[8] 是UB,即使&amp;a[8] == &amp;b[0]
  • @Cedric:第一个成员有特殊待遇。 (我什至不确定以这种方式访问​​a[1] 是否合法,因为演员是WORD* 而不是WORD(*)[8]

标签: c++ pointers struct


【解决方案1】:

在声明中

LAYOUT* chessboard = &chessLayout;

/* ... */

for(int i = 0; sizeof(LAYOUT) / 2;i++){
     printf("%x", *(reinterpret_cast<WORD*>(chessboard+i)));
}

指针chessboard 递增,其类型为LAYOUT*。这将使指针沿“i * sizeof(LAYOUT)”字节移动,而您想要的是移动到下一个 WORD。

因此,表达式应如下所示(我在演员表之外):

for(int i = 0; i < sizeof(LAYOUT) / 2; ++i){
     printf("%x", *(reinterpret_cast<WORD*>(chessboard)+i));
}

编辑:正如@Jarod42 指出的那样,您可能很快就会遇到 UB,因为我们正在迭代数组 a 的“结束迭代器”。

确切的解释有时不清楚,并在 SO 和其他地方引起了许多讨论。

这是一个关于此问题并使用 C++17 offsetof 功能的线程,在这种情况下应该有所帮助:Do we need to use std::launder when doing pointer arithmetic within a standard-layout object (e.g., with offsetof)?

我认为对此的详细讨论很重要,但有足够多的活跃线程,此处不再重复。

【讨论】:

  • 另外:for 循环中的检查总是结果为真。应该是i &lt; sizeof(LAYOUT)/2
【解决方案2】:

为了避免令人兴奋的 UB 探戈,我会以相反的方式执行此操作,让布局包含整个数据的数组,而 a,b,c,d 将是吸气剂。即使像下面的代码中那样强制转换指针也会是边缘行走,但是我们可以遍历 data ,而不用担心它可能会被填充词打断。

using WORD = unsigned short;

namespace LayoutNS { 
      
struct Layout {
    WORD data[64];
    
    template <size_t N>
    using line = WORD [N];
    
    template <size_t N>
    line<N>* operator[] ( line<N>* (*f)(Layout&)  )
    {
        return (*f)(*this);
    }   
};

}

using LAYOUT = LayoutNS::Layout;

// better to avoid such cast either, can we go just with a pointer? Or return a copy?
LAYOUT::line<8>* line_a (LAYOUT& l) { return (LAYOUT::line<8>*)(l.data); }
LAYOUT::line<8>* line_b (LAYOUT& l) { return (LAYOUT::line<8>*)(l.data+8); }
LAYOUT::line<32>* empty (LAYOUT& l) { return (LAYOUT::line<32>*)(l.data+16); }
LAYOUT::line<8>* line_c (LAYOUT& l) { return (LAYOUT::line<8>*)(l.data+48); }
LAYOUT::line<8>* line_d (LAYOUT& l) { return (LAYOUT::line<8>*)(l.data+56); }

int main()
{
    LAYOUT test = {};
    auto test_a = test[line_a];

    std::cout << (*test_a)[1];
}

避免间接级别和 C++11 功能的简化版本:

struct LAYOUT {
    WORD data[64];
       
    WORD* operator[] ( WORD* (*f)(LAYOUT&)  )
    {
        return (*f)(*this);
    }   
};

WORD* line_a (LAYOUT& l) { return (l.data); }
WORD* line_b (LAYOUT& l) { return (l.data+8); }
WORD* empty (LAYOUT& l) { return (l.data+16); }
WORD* line_c (LAYOUT& l) { return (l.data+48); }
WORD* line_d (LAYOUT& l) { return(l.data+56); }

int main()
{
    LAYOUT test = {{1,3,4,5}};
    auto test_a = test[line_a];

    std::cout << test_a[1];
}

【讨论】:

  • 这是一个非常聪明的思考方式!但是,由于结构位于另一个复杂的数据结构中,因此我将保持原样,我也希望保持相对简单。不过还是非常感谢。
  • @Alex 公平点,如果该外部代码严重依赖于实现,我故意这样编写它,它可以用最少的更改来使用,chessLayout.a 将变为chessLayout[line_a] 或类似的嵌套类不需要任何额外的命名空间来隐藏,但这取决于你。
  • @Alex first 是 line 类型的模板,一个大小为 N 的 WORD 数组(using 在 C++11 及更高版本中充当 typedef,可以与模板一起使用).. @ 987654328@ 是作为下标传递的函数指针,它以 Layout& 作为参数并返回指向 line 的指针,例如,它被分配为 line_a。下标运算符返回与传递给它的函数相同的类型。整个事情可能会被内联。
  • 所以在这里,line&lt;N&gt;* 是指向N 单词数组的指针?很抱歉,但我仍在努力理解您在 line_a 函数 (LAYOUT::line&lt;8&gt;*)(l.data); 中所做的事情,您是否将数据转换为长度为 8 的 WORD 数组?
  • @Alex 是的,我将指向 WORD 的指针转换为指向 WORD[N] 的指针。如果您不需要某些静态数据(例如,在其他地方静态确定数组的大小,迭代它),则可能完全可以避免。如果不需要,可以用 WORD* 代替,代码看起来更简单,不需要强制转换
猜你喜欢
  • 2023-02-13
  • 1970-01-01
  • 2016-11-12
  • 2013-09-12
  • 1970-01-01
  • 2019-06-23
  • 2021-11-23
  • 1970-01-01
相关资源
最近更新 更多