3

I have heard that a successful call to malloc() returns a pointer suitably aligned for any type. Yet it seems useless and wasteful to require malloc(1) to return a pointer aligned for a larger value than 1 since no object larger than char can be stored into the block.

What is the alignment requirement for malloc(1), malloc(2), etc.

If the alignment is larger than the allocated size, what is the rationale for such a requirement?

chqrlie
  • 131,814
  • 10
  • 121
  • 189

1 Answers1

4

The definition of the malloc function in the C Standard is terse and does not specify anything regarding alignment. The abstract from the C23 Standard below is substantially similar to previous editions with only a chapter number change:

7.24.3.6 The malloc function

Synopsis

#include <stdlib.h>
void *malloc(size_t size);

Description

The malloc function allocates space for an object whose size is specified by size and whose representation is indeterminate.

Returns

The malloc function returns either a null pointer or a pointer to the allocated space.

Regarding the block alignment requirement, up until C17, this was specified at the beginning of the parent chapter:

7.22.3 Memory management functions
[...]
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).

This meant that malloc(1) must be correctly aligned for all fundamental types such as int, long, long long, etc. and is somewhat ambiguous about accessing such objects in the space allocated.

Reported as a defect, this problem was discussed and the text was amended in C23 to relax this requirement and clarify this ambiguity (emphasis mine):

7.24.3 Memory management functions
[...]
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 size less than or equal to the size requested. It may then be used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).

This amendment is welcome: from C23 on, malloc(1) has no alignment requirement and successive calls to malloc(1) may return successive addresses in a packed array of bytes. Similarly, malloc(2) is no longer required be be suitably aligned for objects of size greater than 2. malloc(3) will be suitably aligned for 2 byte objects such as short on most architectures, etc.

This may cause problems for packages that take advantage of the original alignment requirement to store tags in the low order bits of object pointers returned by malloc(). The size passed to malloc on such systems must be greater than or equal to 2n where n is the number of bits in the tag, typically limited to 3 or 4. A better solution is to use aligned_malloc() where the alignment requirement can be specified and tested.

Note also that the alignment requirement for the block returned by calloc(4, 1) is 4, as the allocated size is the product of the arguments, the first being the number of elements to allocate space for, the second being the element size. Programmers hence do not need to be aware of the arguments' precise semantics. Note that nelems and size are passed in a different order in other C library functions that take such arguments, such as fread(), fwrite(), qsort() and bsearch().

realloc() follows the same requirements: reducing the size of a block may produce a different pointer with a weaker alignment.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    Concerning `calloc(a,b)`, and "less than or equal to the _size_ requested", IMO, the _size_ is `a*b`, using extended math if needed. – chux - Reinstate Monica Jul 05 '23 at 09:34
  • Does the use of `realloc()` affect this? Also, if you call say `malloc(64)` the system has no idea whether you will use this for 64 `char` elements or 8 `double` elements. – Weather Vane Jul 05 '23 at 09:40
  • 1
    @chux-ReinstateMonica: I agree with your point. Shall amend the answer. – chqrlie Jul 05 '23 at 09:40
  • @WeatherVane: the block returned for `malloc(64)` can be used to store all fundamental types with sizes <= 64, as specified, so the system does not need to know. Whereas `aligned_malloc(64, 1)` may return a pointer with byte alignment only. – chqrlie Jul 05 '23 at 09:42
  • But doesn't that lead to say `malloc(1048576)` requiring an alignment of `1048576`? – Weather Vane Jul 05 '23 at 09:44
  • @WeatherVane what would be a fundamental type of size `< 1048576` requiring an alignment of `1048576`? – Gerhardh Jul 05 '23 at 10:47
  • @WeatherVane: no it does not say that: *suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and size less than or equal to the size requested* means the alignment must be appropriate for any object type so long as the object size and alignment is <= 1048576, so 8 bytes on most systems and 16 bytes on those with 128-bit integers that require 16 byte alignment. – chqrlie Jul 05 '23 at 11:15
  • @Gerhardh I asked that, because what fundamental type would be 64 bytes? The comment read to me that the alignment would be the allocation size. – Weather Vane Jul 05 '23 at 14:41
  • 1
    @WeatherVane I can't see where you get that interpretation from that senctence. It's the other way around. The allocation size limits the alignment requirement. It does not extent it. – Gerhardh Jul 05 '23 at 14:46
  • Re: "This may cause problems": "Not only do compilers try to align everything to some multiple of 8, but memory allocator does the same as well." and "Actually, I suppose there is no guarantee about that, but in reality, all allocators do so. For example, GNU libc malloc, even on 32-bit platforms, aligns addresses to multiples of 8." ([source](https://piotrduperas.com/posts/nan-boxing)). He probably needs to read N2293. – pmor Jul 06 '23 at 12:52