从结构数组中提取变量数组

Extract an array of variables from an array of structs

假设我将结构定义为

typedef struct worker{
  char name[20];
  int age;
} worker

然后我创建一个长度为 20 的数组,

worker *business;
business = malloc(20 * sizeof(worker));

是否有任何“自然”(即已经在语言中实现)的方式从这样的数组中获取“age”元素的子数组?

我正在考虑类似

的事情
business.age

其中 business.agebusiness?

每个元素年龄的整数数组
typedef struct worker{
char name[20];
int age;
} worker

worker* business;
business = malloc(20 * sizeof(worker));

当你像上面那样分配结构数组时,age的内存是不连续的,你不能像结构中的元素那样访问它。

/* error, business is an array */
business->age;

如果你想要连续的内存,你必须为 age 使用一个数组,为 name 使用一个数组而不是结构。

char** name = malloc(20 * 20 * sizeof(char));
int* age = malloc(20 * sizeof(int));

您的数组中结构的 age 字段未形成连续的内存区域。内存中的布局类似这样:

              business[0]                          business[1]
[ 20 bytes of name | 4 bytes of age ][ 20 bytes of name | 4 bytes of age][ 20...

您的年龄由名称数组分隔。由于 C 中的数组必须是连续的内存区域,没有间隙,所以不可能只引用年龄的“数组”。但是,您可以使用 for 循环轻松遍历年龄或编写 function/define 宏来访问特定索引处的年龄:

int get_age_at_index(worker* arr, int idx) {
    return arr[idx].age;
}
// OR
#define GET_AGE_AT_INDEX(arr, idx) (arr[idx].age)

这可能允许类似的语义来访问数组元素,如果这是您所追求的:

int my_age = get_age_at_index(business, 5);
// OR
int my_other_age = GET_AGE_AT_INDEX(business, 3);

如果您可以复制数据,您可以为 20 个整数分配一个数组并从每个结构复制年龄:

int ages[20] = { 0 };
for(int i; i < 20; i++) {
    ages[i] = business[i].age;
}

编辑 通过做一些 hacky 指针运算,您可以非常接近您年龄段的数组访问语义。我不建议实际使用它,因为它可能不是 CPU 的最佳选择,而且不如以前的解决方案干净。我想在这里展示它作为一个特点。

当你有一个指向 C 中数组开头的指针时,比如 worker* business 并且你像数组一样对它进行索引 business[index] 下面发生的是指针在数组中“滑动”了一个偏移量。每个元素在数组中占用 sizeof(worker) 内存,因此“滑动”到下一个元素意味着将指针“增加”sizeof(worker)。最后,business[index] 表示“计算值 business + index * sizeof(worker) 并访问该地址处的值,就好像它是一个结构 worker”:

business points to the beginning of array:
[ worker struct 0    ][ worker struct 1    ][ worker struct 2    ]
^                     ^                     ^
  0 * sizeof(worker)    1 * sizeof(worker)    2 * sizeof(worker) <-- offsets from the beginning of array

如果我们使指针等于第一个结构内存中 age 成员的地址,但将其转换为 worker*:

worker* my_hacky_pointer = (worker*) &(business[0].age);

将它增加 1 实际上会将它移动 sizeof(worker),因为这是指针的类型。因此,它将移动到数组下一个元素的 age 成员。如果我们然后将此指针转换回 int 并取消引用它 *(int*) (my_hacky_pointer + 1) 我们将在索引 1.

处获得 age 字段的值

完整示例来源:

#include <stdio.h>
#include <stdlib.h>

typedef struct worker{
  char name[20];
  int age;
} worker;

int main() {
        worker* business = malloc(20 * sizeof(worker));

        business[0].age = 5;
        business[1].age = 6;
        business[2].age = 99;
        business[3].age = 66;
        business[4].age = 78;

        worker* my_hacky_pointer = (worker*) &(business[0].age);
        printf("my age: %d\n", *(int*) (my_hacky_pointer + 0)); // this prints 5
        printf("my age: %d\n", *(int*) (my_hacky_pointer + 1)); // this prints 6
        printf("my age: %d\n", *(int*) (my_hacky_pointer + 2)); // this prints 99
        printf("my age: %d\n", *(int*) (my_hacky_pointer + 3)); // this prints 66
        printf("my age: %d\n", *(int*) (my_hacky_pointer + 4)); // this prints 78
}

我建议不要在任何地方使用它,有更好的方法来获得相同的结果,在 CPU 上更清晰、更容易,但我认为有人会发现用指针算法可以实现的事情很有趣。