Home > other >  Extract an array of variables from an array of structs
Extract an array of variables from an array of structs

Time:05-29

Let's say I define a struct as

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

and then I create an array of lenght 20 of such structures,

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

Is there any "natural" (i.e. already implemented in the language) way to obtain, from such array, the sub-array of the "age" element?

I'm thinking of something along the lines of

business.age

where business.age is the array of integers of the age of each element of business?

CodePudding user response:

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

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

When you allocate an array of struct like above, the memory for age is not continuous, you can't access it like an element in a struct.

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

If you want continous memory, you have to use one array for age and one for name instead of a struct.

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

CodePudding user response:

The age fields of structs in your array do not form a continuous area of memory. The layout in memory is similar to this:

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

Your ages are separated by the name arrays. Since Arrays in C have to be continuous areas of memory without gaps, it is not possible to just refer to an "array" of ages. You can, however, iterate over the ages easily with a for loop or write a function/define a macro to access the age at certain index:

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

This could potentially allow for similar semantics to accessing an element of array, if that is what you are after:

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

If you are ok with copying data, you may just allocate an array for 20 ints and copy age from each struct:

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

EDIT By doing some hacky pointer arithmetic you could get pretty close to semantics of an array access for your ages. I do not recommend actually using it though as it probably is not optimal for the CPU and is less clean than previous solutions. I wanna show it here just as a peculiarity.

When you have a pointer to the beginning of an array in C, like worker* business and you index into it like an array business[index] what happens underneath is that the pointer "slides" across the array by an offset. Each element takes sizeof(worker) memory in the array, so "sliding" to the next element means "increasing" the pointer by sizeof(worker). In the end, business[index] means "Calculate the value business index * sizeof(worker) and access the value at that address as if it was a structure 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

If we make the pointer equal to the address of the age member in memory of first structure, but cast it to a worker*:

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

Increasing it by 1 will actually move it by sizeof(worker) as this is the type of the pointer. thus, it will move to the age member of the next element of the array. If we then cast this pointer back to an int and dereference it *(int*) (my_hacky_pointer 1) we will get the value of the age field at index 1.

Full example source:

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

I advise against using it anywhere, there are better ways to get the same results which are cleaner and easier on the CPU, but thought someone would find it interesting what one can achieve with pointer arithmetic.

  •  Tags:  
  • c
  • Related