Is the Memory Allocated for Struct Members Continguous? What If a Struct Member Is an Array

Is the memory allocated for struct members continguous? What if a struct member is an array?

They will not necessarily be contiguous in memory. This is due to struct padding.

However, in your particular case, it may very well be contiguous. But if you changed the order to something like this:

struct test
{
char gender;
int age;
double height;
}

then they most likely will not be. However, in your particular case, you will still likely get padding after gender, to realign the struct to 8 bytes.


The difference between SoA (Struct of Arrays) and AoS (Array of Structs) would be like this:

SoA:

-----------------------------------------------------------------------------------
| double | double | double | *pad* | int | int | int | *pad* | char | char | char |
-----------------------------------------------------------------------------------

AoS:

-----------------------------------------------------------------------------------
| double | int | char | *pad* | double | int | char | *pad* | double | int | char |
-----------------------------------------------------------------------------------

Note that AoS pads within each struct. While SoA pads between the arrays.

These have the following trade-offs:

  1. AoS tends to be more readable to the programmer as each "object" is kept together.
  2. AoS may have better cache locality if all the members of the struct are accessed together.
  3. SoA could potentially be more efficient since grouping same datatypes together sometimes exposes vectorization.
  4. In many cases SoA uses less memory because padding is only between arrays rather than between every struct.

Does a C struct hold its members in a contiguous block of memory?

No, it is not guaranteed for struct members to be contiguous in memory.

From §6.7.2.1 point 15 in the C standard (page 115 here):

There may be unnamed padding within a structure object, but not at its beginning.

Most of the times, something like:

struct mystruct {
int a;
char b;
int c;
};

Is indeed aligned to sizeof(int), like this:

 0  1  2  3  4  5  6  7  8  9  10 11
[a ][b][padding][c ]

In C how does a array of struct look in memory

An array is a contiguous block of memory in c, so an array of structs will have a size of sizeof(struct b)*number_of_structs. All of the structs are allocated within the array.

Note that in your example sizeof(struct b) is not guaranteed to be 6 because the compiler is allowed to insert padding to byte align the structs - you can use "packed" to avoid this if you like but it may hinder performance. Structure padding and packing

How are struct members allocated in memory?

Short answer: they are allocated with the order as they declared in the struct.


Example:

#include <stdio.h>
#include <string.h>

struct student
{
int id1;
int id2;
char a;
char b;
float percentage;
};

int main()
{
int i;
struct student record1 = {1, 2, 'A', 'B', 90.5};

printf("size of structure in bytes : %d\n",
sizeof(record1));

printf("\nAddress of id1 = %u", &record1.id1 );
printf("\nAddress of id2 = %u", &record1.id2 );
printf("\nAddress of a = %u", &record1.a );
printf("\nAddress of b = %u", &record1.b );
printf("\nAddress of percentage = %u",&record1.percentage);

return 0;
}

Output:

size of structure in bytes : 16 
Address of id1 = 675376768
Address of id2 = 675376772
Address of a = 675376776
Address of b = 675376777
Address of percentage = 675376780

The pictorial representation of above structure memory allocation is given below. This diagram will help you to understand the memory allocation concept in C very easily.

Sample Image


Further reading: check out here (also the source for the above example) for C – Structure Padding and Structure dynamic memory allocation in C.

Allocating contiguous memory to contain multiple structs with flexible array members

You can't have an array of foos, at all, because foo does not have a fixed size, and the defining characteristic of an array is that each object has fixed size and offset from the base computable from its index. For what you want to work, indexing array[n] would have to know the full size of foo[0], foo[1], ..., foo[n-1], which is impossible, because the language has no knowledge of those sizes; in practice, the flexible array member is just excluded from the size, so foo[1] will "overlap" with foo[0]'s data.

If you need to be able to access these objects as an array, you need to give up on putting a flexible array member in each one. Instead you could put all the data at the end, and store a pointer or offset to the data in each one. If you don't need to be able to access them as an array, you could instead build a sort of linked list in the allocated memory, storing an offset to the next entry as a member of each entry. (See for example how struct dirent works with getdents on most Unices.)

Array of structure contiguous in memory

You have the right idea. The code you have written allocates space for one struct with a variable-size array using a C99 style flexible member. The same trick would work with a C90-style zero-length array or 1-sized array.

The struct commonly includes a member to specify the size of the variable-length portion, though that is obviously not a requirement. But you can see how this is a problem for making an array of these structs.

Generally

structure_t my_array[5];
...
my_array[2].a = 0.0;

is the same as

(structure_t*)((void*)my_array + 2*sizeof(structure_t))->a = 0.0;

But what's the sizeof our dynamic struct? Whatever it happens to evaluate to, it is clearly what we want, since it does not include the variable portion.

So we can allocate the array of these structs by doing

void *result = malloc(arraysize * dimstruct);

and it will be contiguous. But we can't use array indexing syntax, since the size of the struct is undefined. Instead we index manually, as hinted above:

structure_t *p = result + i * dimstruct;
p->a = 0.0;

Note that result is a void *. If we declared it as structure_t * the offset calculation would be wrong since it would use multiples of sizeof(structure_t)

Using Contiguous Memory of C Struct Members


How is this meant to work if fields in a struct aren't guaranteed to be contiguous?

The standard doesn't require structs to be contiguous, but this doesn't mean that structs are laid out at random or in unpredictable ways. The specific compiler and linker being used will always generate the binary in a specified way, as dictated by the Application Binary Interface or ABI. It just so happens that on a GNU/Linux machine, the ELF ABI exactly corresponds to how GCC will lay out and access that struct.

In other words, you can predict whether the method you describe will work for any given ABI / compiler / linker combination. It's not guaranteed to work by the standard, but it might be guaranteed to work by the compatibility of ABIs.

Difference between memory allocations of struct member (pointer vs. array) in C

There is a difference.

I'll try to illustrate the sample.

As others pointed out:

  1. First example is harder to manage, but you can change size of the buffer any time.
  2. Second example is easier to manage (you don't have to care about freeing buffer separately), but you can only have fixed size of buffer.

As pointed out in comments: In case if buffer is a last element in a structure (like in the example provided) it is possible to allocate any length for the buffer.

For example

int extra_bytes_needed = ...;
Container* container = calloc(sizeof(Container) + extra_bytes_needed, 1);

On the left side of image - your first case.

On the right side of image- your second case.

Left is first case, Right is second case.

Memory Allocation in an array of structures

OP's code biggest failing is not allocating memory for each airport[i]


With airPdata **airport and I want to use an array of pointers, code needs to allocate at 2 levels and use an arrray.

Memory for array airport[]

Memory allocated & assigned to each element of airport[i] (OP missed this part.)

Memory allocated & assigned to the various members like airport[i].LocID


Memory for array airport is easy as shown below. airPdata **airport is a pointer and not an array. Instead use an array as that is the stated design goal.

// define array element count. 
#define AIRPORT_N 100
// Declare the array.
airPdata *airport[AIRPORT_N];
// Keep tack of how much of the array is used.
size_t n = 0;

Now assign, read and begin filling the array, allocating as needed.

#define AIRPORT_STRING_SIZE (50 + 1)
char line[1024];
while(n < AIRPORT_N && fgets(line, sizeof line, fp)) {
// Allocate memory for one element of `airport`
// Notice no cast nor type coded here.
airport[n] = malloc(sizeof *(airport[n]));
if (airport[n] == NULL) {
// Something simple for now.
fprintf(stderr, "OOM\n");
break;
}
// Create space for each string,
// TODO: add check for Out-of-Memory
airport[n]->LocID = malloc(AIRPORT_STRING_SIZE);
airport[n]->fieldName = malloc(AIRPORT_STRING_SIZE);
airport[n]->city = malloc(AIRPORT_STRING_SIZE);

// Code to parse `line` into `airport[n]` members.

// Usually the parsing happens first and if successful, the above allocations occur.

// If the `LocID` string (and others) need not change then
// use below to allocate a right-sized memory
// after parsing instead of allocating to some max size, like above.
airport[n]->LocID = strdup(LocID_string);

n++;
}

Later free it all

for (size_t i = 0; i < n; i++) {
free(airport[i]->LocID);
free(airport[i]->fieldName);
free(airport[i]->city);
free(airport[i]);
}

Detail: Notice the subtle error in the following. It allocates to the size of airport, which is type airPdata **.

Instead, it should allocate to the size of * airport, which is type airPdata *.

Very commonly, object pointers of all types are the same size, yet this same-ness is not specified across all type in C.

Better to allocate to the size of the de-referenced pointer that to a type. It more likely coded right, easier to review and maintain.

// airPdata **airport = malloc(sizeof(airport) * (50+1));
airPdata **airport = malloc(sizeof *airport * (50+1));

Memory Allocation for an Array of Struct and Class Object

Perhaps the difference between an array of a reference type and an array of a value type is easier to understand with an illustration:

Array of a reference type

Array of a reference type

Each Point as well as the array is allocated on the heap and the array stores references to each Point. In total you need N + 1 allocations where N is the number of points. You also need an extra indirection to access a field of a particular Point because you have to go through a reference.

Array of a value type

Array of a value type

Each Point is stored directly in the array. There is only one allocation on the heap. Accessing a field does not involve indirection. The memory address of the field can be computed directly from the memory address of the array, the index of the item in the array and the location of the field inside the value type.



Related Topics



Leave a reply



Submit