按地址访问结构中的元素
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) = 5
和customers[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;
它可以编译得很好,并且可能会工作[*],但它是未定义的行为,因为你不能仅仅因为一个结构由四个int
s 组成,就认为它在内存中的布局方式与 @ 相同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));
【讨论】:
我不相信结构中相同类型的相邻值之间会有填充,尽管我希望被证明是错误的。 对于缺少桶形移位器的旧处理器,这种填充是必要的。 我看不出桶形移位器与此有什么关系?我说的是同一类型的两个值之间的填充...以上是关于按地址访问结构中的元素的主要内容,如果未能解决你的问题,请参考以下文章