-1

I'm trying to implement a new malloc that stores the size at the front of the malloc'ed region, and then returns a pointer to the incremented location (what comes after the stored unsigned int).

void* malloc_new(unsigned size) {
    void* result = malloc(size + sizeof(unsigned));
    ((unsigned*)result)[0] = size;
    result += sizeof(unsigned);
    return result;
}

I'm having doubts regarding whether the

result += sizeof(unsigned);

line is correct (does what I want). Say the original address in the heap for the malloc is X, and the size of unsigned is 4, I want the 'result' pointer to point to X + 4, right? Meaning that the memory location in the stack that stores the 'result' pointer should contain (the original heap address location + 4).

  • 2
    When you are learning C, I would recommend to use the gcc compiler and always compile as `gcc -std=c11 -pedantic-errors -Wall -Wextra`. That way you don't end up with questionable non-standard code. – Lundin Mar 11 '19 at 08:55

4 Answers4

2

result += sizeof(unsigned); should give you at least a warning (pointer arithmetic on void * leads to undefined behavior).

unsigned *result = malloc(size + sizeof size);
result[0] = size;
return result + 1;

should be the easier way.

Please note that the returned memory is not well aligned for all possible datatypes. You will run into troubles if you are using this memory for double or other 64bit datatypes. You should use an 8 byte datatype uint64_t for storing the size, then the memory block afterwards is well aligned.

mch
  • 9,424
  • 2
  • 28
  • 42
  • 2
    Actually, you probably want `result` to be a pointer to union of `size_t` and `max_align_t` to ensure the alignment (`max_align_t` could be wider than `uint64_t`, even on platforms where the latter is defined). – Toby Speight Mar 11 '19 at 12:40
2

In addition to the problems noted in other answers with performing pointer arithmetic on void * pointers, you're also likely violating one of the restrictions the C standard places on memory returned from functions such as malloc().

7.22.3 Memory management functions, paragraph 1 of the C standard states:

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). The lifetime of an allocated object extends from the allocation until the deallocation. Each such allocation shall yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer is returned. If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.

Note the bolded part.

Unless your system has a fundamental alignment that's only four bytes (8 or 16 is much more typical), you are violating that restriction, and wil invoke undefined behavior per 6.3.2.3 Pointers, paragraph 7 for any object type with a fundamental alignment requirement larger than four bytes:

... If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. ...

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
0

How void pointer arithmetic is happening in GCC

C does not allow pointer arithmetic with void * pointer type.

GNU C allows it by considering the size of void is 1.

The result is void* so result += sizeof(unsigned); just happens to work on compatible compilers.

You can refactor your function into:

void *malloc_new(unsigned size) {
    void* result = malloc(size + sizeof(unsigned));
    ((unsigned*)result)[0] = size;
    result = (char*)result + sizeof(unsigned);
    return result;
}

Side note, before void type and void* generic pointer existed in the C language, programmers used char* to represent a generic pointer.

Community
  • 1
  • 1
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
-1

You can do void* arithmetic if you cast the type first to char*, for instance. And then cast back to void*. To get better alignment, use 64 bit type for the size, e.g. uint64_t.

#define W_REF_VOID_PTR(ptr,offset) \
    ((void*)((char*) (ptr) + (offset)))
J.P.
  • 9
  • 3