按地址访问结构中的元素

Access elements in Struct by Address

本文关键字:元素 结构 地址 访问      更新时间:2023-10-16

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

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;
}

我原以为我能做到这一点,但我犯了各种错误。知道"瓶子"的值将是内存中距离客户地址的第二个空间,我就不能直接设置位置了。

我知道这可能是不合适的代码,但我想了解它是如何工作的以及为什么不工作。我还保证,我有理由尝试用传统的方式来做这件事,哈哈

与其使用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

你不能这样做:

*(&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组成,就认为它在内存中的布局与int[4]相同。它们的布局相同似乎是合理的,甚至是合乎逻辑的,但标准并不要求它,所以就是这样。很抱歉

正如@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);
  }
}

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

&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不一定等于CCD_ 24。

所以你需要做一些类似的事情

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));