3

Often malloc is used to allocate memory for n elements of some primitive datatype (int, float, char). This could look like this:

#define N 10

double *mem = malloc(N * sizeof(double));

for (int i = 0; i < N; ++i) {
  mem[i] = 10.0;       // Write
  double d = mem[i];   // Read
}

This would even work if I declared mem as char * and used a cast later on:

char *mem = malloc(N * sizeof(double));

for (int i = 0; i < N; ++i) {
  *(double*)(mem + i * sizeof(double)) = 10.0;
  double d = *(double *)(mem + i * sizeof(double));
}

Currently I'm wondering if this is well defined and working for every datatype (even any complex type like struct). Lets assume struct a could be anything. Is the following well defined?

char *mem = malloc(N * sizeof(struct a));

for (int i = 0; i < 10; ++i)) {
  *(struct a *)(mem + i * sizeof(struct a)) = /* something valid */;
  struct a x = *(struct a *)(mem + i * sizeof(struct a));
}

Since the return value of malloc is suitably aligned for any kind of variable the case i = 0 is definitely well defined and working, but how about I = 1, 2, ...? Will alignment screw this up? Like in this example:

char char_arr[4] = /* some value */;
int *d = &char_arr[0];

The second line is not guaranteed to work, since char_arr may not be aligned on a 4-byte boundary (required for int).

user7802048
  • 183
  • 6
  • 1
    Unless the structure is packed, it will itself contain padding making it aligned. Otherwise, you could never dynamically allocate arrays of structures using `malloc`. – Some programmer dude Apr 06 '17 at 13:32
  • @Someprogrammerdude And by default the structure includes padding, right? In some sense the `sizeof` operator takes care right, since its including the padding? – user7802048 Apr 06 '17 at 13:37
  • 1
    Yes, `sizeof` will returns the full size of the structure, including padding. – Some programmer dude Apr 06 '17 at 13:38
  • There are cases where it is tricky. In case of an array of packed structures, whose size is even, there could be a problem accessing the array unless specified that both array and structure are packed (without padding bytes). When allocating memory you have to consider also the space that the compiler would have used for padding the array. – Frankie_C Apr 06 '17 at 13:43
  • 1
    For the latter the code has other issues, too. It violates the effective type rule, thus invokes undefined behaviour. The `malloc` parts become "problematic" (actually UB, too) if you access the allocated block via the `char *`, then cast to another type. Why don't you use the correct type for the pointer? Why `char *`? `malloc` & friends return `void *`, not `char *`. – too honest for this site Apr 06 '17 at 13:55
  • Since the line `struct a = *(struct a *)(mem + i * sizeof(struct a));` doesn't compile, what happens at runtime won't matter. You missed a variable name, but it isn't clear what you're expecting it to do. In general, what you're doing isn't a clear coding style, so it isn't good. All else apart, code using the `*(ptr + offset)` notation, especially with casts, is pretty nasty. It will probably work, though. – Jonathan Leffler Apr 06 '17 at 14:00
  • @Olaf Could you elaborate the *effective type rule*? I couldn't find anything on google.. I use `char *`, since the code is used in a data structure, which should be capable to hold any kind of data. And the cast from `char *` to another type pointer is not UB (see @chux answer). – user7802048 Apr 06 '17 at 14:09
  • @JonathanLeffler Good catch, I added the variable name. Thank you – user7802048 Apr 06 '17 at 14:12
  • 1
    @user7802048 `char *` is not " capable to hold any kind of data". Casts from _any_ `char *` to another type pointer can be UB. Your question was about pointer from `malloc()`. The _higher_ level coding goal remains unclear. Knowing that may provide even better answers. – chux - Reinstate Monica Apr 06 '17 at 14:17
  • @chux Sorry, I meant from `char *` but returned from `malloc`. – user7802048 Apr 06 '17 at 14:18
  • Hmm, you must be using a different google than me. However, it is in the standard and modern compilers like gcc rely on code not violating it for optimisations. "which can hold any kind of data" would first mean `void *`, not `char *`. But in case you consider using the same block for different types: this definitively violates the effect ive type rule. Briefly: **don't do it!** Don't work against your compiler or the language! Type-checking is good for code-quality, don't defy it. And read my comment **carefully** it is not UB **per se**, but it is a thin line. – too honest for this site Apr 06 '17 at 14:22
  • @user7802048 Effective type for `malloc`'d area won't be a problem unless you violate aliasing rule (for example, set value through `double*`, but read it through `int *`). So you can hold the pointer in `char*` type. [See this answer for details](http://stackoverflow.com/a/41338286/694733). But again, this applies only on `malloc`, your last example with `char_arr` won't work as you already know. – user694733 Apr 07 '17 at 06:45

2 Answers2

1

Yes, you can use malloc to allocate array of structures.

7.22.3 Memory management functions

  1. The order and contiguity of storage allocated by successive calls to the aligned_alloc, calloc, malloc, and realloc functions is unspecified. The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated). ...

But you don't need to do all that ugly casting:

struct a *mem = malloc(N * sizeof(struct a));

for (int i = 0; i < 10; ++i)) {
  mem[i] = /* something valid */;
  struct a var = mem[i];
}
user694733
  • 15,208
  • 2
  • 42
  • 68
  • In my question I even say that the pointer (returned by `malloc`) is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment required, but how about the second, third, etc elements? They are not directly accessed as the pointer of returned by `malloc`. Will this still work? Maybe I'm asking if the shift of `sizeof(struct a)` will always end up aligned just right? – user7802048 Apr 06 '17 at 14:04
1

Is the following well defined?

char *mem = malloc(N * sizeof(struct a));

for (int i = 0; i < N /* 10 */; ++i)) {
  *(struct a *)(mem + i * sizeof(struct a)) = /* something valid */;
  // struct a = *(struct a *)(mem + i * sizeof(struct a));
  struct a x = *(struct a *)(mem + i * sizeof(struct a));
}

Almost always.

In terms of alignment, *alloc() returns a pointer to memory that is valid for all fundamental alignments as OP noted. (struct a *)(mem + i * sizeof(struct a)) will also provide a well aligned pointer for all 0 <= i <= N.

Not a likely concern for OP, yet on rare machines i * sizeof(struct a) will overflow size_t math (with a large enough i and sizeof(struct a), whereas mem[i] will not. This is not seen on common implementations that use a flat memory address.


Also robust code checks for memory allocation failures.


Candidate simplified code. Notice the type ptr points to is not relevant, assuming it is not defined void *ptr. @usr.

ptr = malloc(sizeof *ptr * N);
if (ptr == NULL) Handle_OOM();

for (size_t i = 0; i < N; ++i)) {
  ptr[i] = /* something valid */;
}

`

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • "Notice the type ptr pointers to is not relevant" -- it's relevant if `ptr` is of type `void*` ;-) – P.P Apr 06 '17 at 14:02
  • Thank you! That is what I was looking for. So it's right to say if `ptr` is suitably aligned to point to `struct a`, `ptr + sizeof(struct a)` is well suitably aligned as well? – user7802048 Apr 06 '17 at 14:07
  • @user7802048 `char *ptra; ptra + sizeof(struct a)` is OK. Curiously, why do you want to code that way? What is the coding goal does that meet well? – chux - Reinstate Monica Apr 06 '17 at 14:12
  • I'm trying to create a data structure capable to hold any kind of data (internally it uses `malloc` to allocate enough memory to hold some elements and reallocates more later on). The pointer is stored as `char *` for internal pointer arithmetic (shift by element size) and the data is returned as `void *`. – user7802048 Apr 06 '17 at 14:16
  • Do not used `char *` to store the data. Use a union [example](http://stackoverflow.com/a/42299077/2410359) of a `void *` and function pointer. This only then works for pointers, A pointer is too small to store "any kind of data" such as `long double complex`. – chux - Reinstate Monica Apr 06 '17 at 14:22