按地址访问结构中的元素

Posted

技术标签:

【中文标题】按地址访问结构中的元素【英文标题】:Access elements in Struct by Address 【发布时间】:2015-02-22 01:52:26 【问题描述】:

我已经进行了一些研究,但在这里或谷歌上找不到我想要的东西。有没有办法通过地址(而不是使用 customer[i].bottles)访问 Customer 中的元素。我无法修改结构,因此无法将属性放入数组中。

typedef struct Customer 
  int id;
  int bottles;
  int diapers;
  int rattles;
 Customer;

Customer customers[100];

void setValue(int custInd, int propertyInd) 
  //propertyInd would be 1 for id, 2 for bottles
  //Attempting to set customers[0].bottles
  *(&customers[custInd]+propertyInd) = 5;

我以为我能做到这一点,但我遇到了各种错误。知道“瓶子”值将是客户地址中内存中的第二个空间,我不应该能够直接设置该位置。

我知道这可能是不正确的代码,但我想了解它是如何工作以及为什么工作/不工作。我还保证我有理由尝试以传统方式进行此操作哈哈

【问题讨论】:

这个*(&customers[custInd]+propertyInd) = 5customers[custInd+propertyInd] = 5基本一样,你为什么不直接做switch (propertyInd) case 0: customers[custInd].id = 5; default: break'呢? C 还是 C++?选择一个 【参考方案1】:

可能不使用propertyInd,而是将偏移量传递到结构中。这样,即使布局发生巨大变化(例如,如果它在开头包含非 int 字段),代码也能正常工作。

你可以这样做:

void setValue(int custInd, int fieldOffset) 
    int *ptr = (int *)((char *)&customers[custInd] + fieldOffset);
    *ptr = 5;


...
setValue(custInd, offsetof(Customer, bottles));

offsetof 是一个标准化的宏,它返回从结构开始到给定元素的偏移量(以字节为单位)。

如果您仍想使用索引,则可以将偏移量计算为propertyInd * sizeof(int),假设结构中的每个字段都是int

【讨论】:

如果你愿意做出最后一段的假设,你可以写成((int*)&customers[custInd])[propertyInd] = 5,更简单一点。但是aiui,不能在兼容的C中做出这种假设。 非常感谢!这很棒。您能解释一下为什么我们先将类型转换为 char 然后再转换为 int 吗? 我们类型转换为char *,这样我们就可以添加偏移量,然后类型转换为int *,这样我们就有了正确的指针类型。【参考方案2】:

你不能这样做:

*(&customers[custInd]+propertyInd) = 5;

因为&customers[custInd] 的类型是struct Customer*,而不是int *。所以&customers[custInd]+propertyInd&customers + custInd + propertyInd 的含义相同,或者换句话说,&customers[custInd + propertyInd]。然后赋值尝试将结构值设置为整数5,这显然是非法的。

我猜你的意思是

((int*)&customers[custInd])[propertyInd] = 5;

它可以编译得很好,并且可能会工作[*],但它是未定义的行为,因为你不能仅仅因为一个结构由四个ints 组成,就认为它在内存中的布局方式与 @ 相同987654331@ 将是。它们的布局相同似乎是合理甚至合乎逻辑的,但标准并不要求它,就是这样。对不起。

正如@iharob 在评论中建议的那样,您可能会发现编译器足够聪明,可以从以下措辞生成高效的代码:

void setValue(int custInd, int propertyInd, int value) 
  //propertyInd would be 1 for id, 2 for bottles
  switch (propertyInd) 
    case 1: customers[custInd].id = value; break;
    case 2: customers[custInd].bottles = value; break;
    case 3: customers[custInd].diapers = value; break;
    case 4: customers[custInd].rattles = value; break;
    default: assert(0);
  

*:实际上,如果 propertyInd for id 为 0,而不是 1,它(可能)会起作用。C 数组索引从 0 开始。

【讨论】:

【参考方案3】:

&customers[custInd] 是指向customers[custInd] 的指针,所以&customers[custInd]+propertyInd 是指向customers[custInd+propertyInd] 的指针。它不是指向成员的指针。它将具有指向Customer 的类型指针。该指针的值将等于&(customers[custInd+propertyInd].id),但不是指向 int 的指针 - 因此编译器错误。

您更大的问题是结构中的四个int 不一定像int 的数组那样布置 - 结构成员之间可能存在填充。所以,如果我们这样做

int *p = &(customers[custInd].id);

那么p+1不一定等于&(customers[custInd].bottles)

所以你需要做类似的事情

void setValue(int custInd, int Offset)

    int *ptr = (int *)(((char *)&customers[custInd]) + Offset);
    *ptr = 5;


/*  and to call it to set customers[custInd].bottles to 5 */

setValue(custInd, offsetof(Customer, bottles));

【讨论】:

我不相信结构中相同类型的相邻值之间会有填充,尽管我希望被证明是错误的。 对于缺少桶形移位器的旧处理器,这种填充是必要的。 我看不出桶形移位器与此有什么关系?我说的是同一类型的两个值之间的填充...

以上是关于按地址访问结构中的元素的主要内容,如果未能解决你的问题,请参考以下文章

支持按索引和键随机访问、以对数时间插入、删除并保持顺序的数据结构

关联容器

关联容器

主存逻辑上是一维线性结构吗

关联容器

数组介绍