当前位置: 首页 > 知识库问答 >
问题:

索引到结构中合法吗?

赵俊侠
2023-03-14

不管代码有多“糟糕”,并且假设对齐等在编译器/平台上不是问题,这种行为是未定义的还是损坏的?

如果我有这样的结构:-

struct data
{
    int a, b, c;
};

struct data thing;

abc 访问为

在每一种情况下,在我尝试的每一个编译器和平台上,我尝试的每个设置都“有效”。我只是担心编译器可能没有意识到b和thing[1]是同一个东西,存储到“b”的可能被放入寄存器,thing[1]从内存中读取错误的值(例如)。但在每一种情况下,我都尝试过,但都做了正确的事情。(我当然意识到这并没有太多证明)

这不是我的代码;这是我必须处理的代码,我对这是坏代码还是坏代码感兴趣,因为不同会影响我更改它的优先级:)

标记为C和C。我最感兴趣的是C,但如果C不同,也是C,只是为了兴趣。


共有3个答案

高嘉树
2023-03-14

在C中如果真的需要的话——创建操作符[]:

struct data
{
    int a, b, c;
    int &operator[]( size_t idx ) {
        switch( idx ) {
            case 0 : return a;
            case 1 : return b;
            case 2 : return c;
            default: throw std::runtime_error( "bad index" );
        }
    }
};


data d;
d[0] = 123; // assign 123 to data.a

它不仅保证工作,而且用法更简单,您无需编写不可读的表达式

注意:给出这个答案的前提是假设您已经有一个带字段的结构,并且您需要通过索引添加访问。如果速度是一个问题,您可以改变结构,这可能会更有效:

struct data 
{
     int array[3];
     int &a = array[0];
     int &b = array[1];
     int &c = array[2];
};

这个解决方案将改变结构的大小,因此您也可以使用方法:

struct data 
{
     int array[3];
     int &a() { return array[0]; }
     int &b() { return array[1]; }
     int &c() { return array[2]; }
};

卫昊东
2023-03-14

不。在C中,即使没有填充,这也是未定义的行为。

导致未定义行为的原因是越界访问1。当您有一个标量(结构中的成员a、b、c)并尝试将其用作数组2来访问下一个假设元素时,您会导致未定义行为,即使该地址碰巧有另一个相同类型的对象。

但是,您可以使用结构对象的地址并计算到特定成员中的偏移量:

struct data thing = { 0 };
char* p = ( char* )&thing + offsetof( thing , b );
int* b = ( int* )p;
*b = 123;
assert( thing.b == 123 );

这必须对每个成员单独执行,但可以放入类似数组访问的函数中。

< sup>1(引自:ISO/innux 6 . 5 . 6加法运算符8)
如果结果比数组对象的最后一个元素多一位,则它不能用作一元*运算符的操作数。

< sup>2(引自:ISO/x 6 . 5 . 6加法运算符7)
对于这些运算符,指向非数组元素的对象的指针的行为与指向长度为1的数组的第一个元素的指针的行为相同,其元素类型为对象的类型。

壤驷德宇
2023-03-14

这是非法的1。这是C语言中未定义的行为。

您以数组的方式获取成员,但这是C标准所说的(强调我的):

[数氯阵列/1]: ...数组类型的对象包含一个连续分配的非空 N 个子对象集,这些子对象类型为 T...

但是,对于成员来说,没有这样的连续要求:

[class.mem/17]:…;实现对齐要求可能会导致两个相邻的成员不会立即在彼此之后分配…

虽然上面的两个引号应该足以说明为什么像你一样索引到结构中不是C标准定义的行为,但让我们选择一个例子:看看表达式

[expr.post//expr.sub/1]:后缀表达式后跟方括号中的表达式是后缀表达式。其中一个表达式应该是“T数组”类型的glvalue或“T指针”类型的prvalue,另一个应该是无作用域枚举或整数类型的prvalue。结果是“T”类型。“T”类型应该是完全定义的对象类型。66表达式E1[E2](根据定义)与((E1)(E2))相同

深入研究上面引用的粗体文本:关于向指针类型添加整数类型(注意此处的强调)。

[expr.add/4]:当具有整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。如果表达式P指向具有n个元素的数组对象x的元素x[i],则表达式P JJ P(其中J具有值j)指向(可能是假设的)元素x[i j]if0≤i j≤n;否则,行为未定义。…

注意if子句的数组要求;否则在上面的引号中。表达式

附带说明:虽然我已经在各种编译器上对代码及其变体进行了广泛的实验,并且他们没有在这里引入任何填充,(它有效);从维护的角度来看,代码非常脆弱。您仍然应该断言实现在执行此操作之前连续分配了成员。并保持在边界内:-)。但它仍然是未定义的行为……

其他答案已经提供了一些可行的解决方法(具有已定义的行为)。

正如我在评论中正确指出的那样,我之前编辑的[basic.lval/8]不适用。谢谢@2501和@M.M。

1:请参阅@Barry对这个问题的回答,这是唯一一个可以通过此部分访问事物结构成员的法律案例。

 类似资料:
  •   a   rt_alarm_container    rt_i2c_bus_device    rt_pm_ops    rt_timer    rt_alarm_setup    rt_i2c_bus_device_ops    rt_pwm_configuration    rt_uart_ops    addrinfo    rt_completion    rt_i2c_msg    r

  • 主要内容:引子,一、索引,二、mysql中索引的数据结构,三、源码,五、总结引子 说几句题外话,在京被困三个月之久,不能回家,所以这个源码分析就中断了。之所以在家搞这个数据库的源码分析,主要是在家环境齐全,公司的电脑老旧不堪。意外事件往往打断正常的习惯和运行轨迹,但这却是正常现象。回来也有两周,从本周开始恢复这个源码分析的系列。 大德久远,有始有终! 一、索引 什么是索引?索引有什么作用?还记得上小学时,老是教使用字典么?如果一个字不认识或者知道读音但字儿不会写都可以通过

  • 我想将多个结构列合并成一个数组。 我从..尝试了数组(col1,col2),但结果是数据类型不匹配,即使所有结构列都是相同的类型。 查询- < code > select array(struct(f_name _ add,True as is_data_found),struct(l_name_add,True as is_data_found))作为标记from (select array(m

  • 在 Solidity 中,合约类似于面向对象编程语言中的类。 每个合约中可以包含 状态变量、 函数、 函数修饰器、事件、 结构类型、 和 枚举类型 的声明,且合约可以从其他合约继承。 状态变量 状态变量是永久地存储在合约存储中的值。 pragma solidity ^0.4.0; contract SimpleStorage { uint storedData; // 状态变量

  • 合并两个已有的索引比重新对所有数据做索引更有效率,而且有时候必须这样做(例如在“主索引+增量索引”分区模式中应合并主索引和增量索引,而不是简单地重新索引“主索引对应的数据)。因此indexer有这个选项。合并索引一般比重新索引快,但在大型索引上仍然不是一蹴而就。基本上,待合并的两个索引都会被读入内存一次,而合并后的内容需要写入磁盘一次。例如,合并100GB和1GB的两个索引将导致202GB的IO操

  • 问题内容: 我有一个在轴1(列)中具有层次结构索引的数据框(来自操作): 我想将其展平,使其看起来像这样(名称不是关键的,我可以重命名): 我该怎么做呢?(我已经尝试了很多,无济于事。) 根据建议,这是字典形式的头 问题答案: 我认为最简单的方法是将列设置为顶级: 注意:如果to级别具有名称,你也可以通过此名称访问它,而不是0。 如果要将 组合成一个索引(假设你的列中仅包含字符串条目),则可以: