0

I am curious of how malloc() actually allocates memory. I am reading C programming by K.N.King for reference. In particular, chapter 17. Initially in the chapter void *malloc(size_t size) is described as function which allocates a block of memory of size bytes and returns a void * pointer to this memory block. Two main applications being dynamically allocating strings and arrays. When malloc() is called the returned pointer will be cast to the appropriate type, for example in,

int *n;
n = malloc(sizeof(*n));

the malloc() return will be cast to (int *). It was my understanding that since this cast occurs the memory block allocated by the call of malloc() contains uninitialised integers. However, after more reading in the chapter I think I am wrong but I can't figure out exactly what is going on.

The conflict in understanding occurred when reading the last section of the chapter on flexible array members. The idea of defining a structure with the last member being "incomplete", i.e.,

struct vstring {
    int len;
    char chars[];
};

The author then goes on to say, and I quote:

A structure that contains a flexible array member is an incomplete type. An incomplete type is missing part of the information needed to determine how much memory it requires. ... In particular, an incomplete type can't be a member of another structure or an element of an array. However, and array may contains pointers to structure that have a flexible array member.

So clearly my earlier understand must be flawed otherwise a call such as,

struct vstring *str = malloc(sizeof(struct vstring) + n);

would allocate an array containing an incomplete type.

Is the block of memory allocated by malloc() an array of a particular type after being cast? If not, then how can the following work,

struct node {
    int value;
    struct node *next;
};

struct node *new_node = malloc(sizeof(*new_node));
new_node->value = 10;

if the memory allocated by the malloc() call is not declared as elements of struct node? Even the integer array example I put at the beginning of the post, I would be able to access the elements of the the allocated memory by subscripting n immediately.

8
  • 1
    How malloc is implemented is operating system specific. On Linux, it often uses mmap(2) and you could download then study the source code of musl libc or of GNU libc. Learn also to use valgrind Commented Jun 4, 2022 at 11:42
  • Read also en.wikipedia.org/wiki/Flexible_array_member Commented Jun 4, 2022 at 11:44
  • The book is wrong when it says that a structure type with a flexible array member is an incomplete type. The type of the flexible array member itself is incomplete, but the type of the structure containing it is not. Structure types containing FAMs have some special rules and restrictions, but they can be used in several ways that incomplete types cannot. For example, they are acceptable operands for the sizeof operator. Commented Jun 4, 2022 at 13:23
  • @JohnBollinger I have commented further on Eric's post. According to the text, the compiler does not know how much memory to allocate to chars[] prior to explicitly allocating memory to the structure. In that sense, is the structure not an incomplete type? In particular, this would imply that the compiler doesn't know the number of bytes required for the structure and sizeof(struct vstring) would fail? Commented Jun 5, 2022 at 0:39
  • @Zeta-Squared: The standard’s wording for flexible array members is a kludge. C 2018 6.2.5 1 indicates a type is incomplete at a point in a translation unit if the information about the type lacks sufficient information to determine the size of objects of that type. By that criterion, a structure with a flexible array member is incomplete. But 6.7.2.1 18 says the size of the structure is “as if” the flexible array member were omitted except there may be more trailing padding. So, for this purpose, the structure does have a known size. For most purposes of the standard, the type is complete. Commented Jun 5, 2022 at 0:44

1 Answer 1

3

the malloc() return will be cast to (int *).

There is no cast in n = malloc(sizeof(*n));. A cast is an explicit operator in source code, the way a plus sign is an operator for addition or an asterisk is an operator for multiplication. A cast is a type name in parentheses. In n = malloc(sizeof(*n));, the value returned by malloc is automatically converted to the type of n, which is int *. This is a conversion, not a cast.

Is the block of memory allocated by malloc() an array of a particular type after being cast?

The memory allocated by malloc has no declared type. For formal semantic and technical reasons in the C standard, it takes on an effective type once you store data into it. This is largely irrelevant to ordinary use as long as you are not trying to do anything “funny” with the memory, such as using it as different types at different types.

If not, then how can the following work…

Once you have used malloc to allocate sufficient memory for an object, you may store a value for that object into the allocated memory.

… if the memory allocated by the malloc() call is not declared as elements of struct node?

The effective type of the memory is determined by the type of the lvalue expression used to store to the memory. An lvalue is an expression that potentially designates an object. For a declared object, as with int x;, the name of the object, x, is an lvalue expression for it. When we have a pointer, as with int *p;, then *p is an lvalue expression for the object that p points to (assuming it is a valid pointer to memory for such an object, not a null pointer or an invalid pointer).

Have assigned new_node to point to memory allocated with malloc, then *new_node is an lvalue expression for the appropriate type, struct node, and new_node->value is an lvalue expression for the int member of struct node. Using these lvalue expressions informs the compiler about how to treat the memory at those locations.

Sign up to request clarification or add additional context in comments.

4 Comments

Ok, I think I now understand. So the memory allocated by malloc is somewhat of a "blank canvas" in that it's converted to a type of the lvalue. Which makes sense to me in the struct node case, since we can consider the memory block allocated to act somewhat like an array, that is, iterating the pointer new_node will iterate through the elements of the block. However, I still don't see why this doesn't hold for FAMs. In that case, the memory block does not get iterated through elements of FAMs but rather the the chars[] member of a single FAM structure. Why is it different for FAMs?
I think I answered my comment by re-reading the section: "The length of the chars array isn't determined until memory is allocated for a vstring structure, normally using a call of malloc" So the compiler does not know how much memory struct vstring requires until we assign it because of the chars[] member. In which case a call of: struct vstring *str = malloc(sizeof(struct vstring) + n); will allocated the size of bytes passed to malloc, of which 4 bytes will be allocated to the int member of vstring and the remainder to chars[].
With this in mind, would that not mean that @John Bollinger's comment on the original post is incorrect, since the compiler doesn't know the number of bytes requires for struct vstring prior to allocating it memory?
@Zeta-Squared, you seem to have the idea. I would suggest, however, that instead of thinking in terms of how much space a FAM requires, think in terms of how much space it has, which can vary from instance to instance of the host struct, and may be zero. In the event of a reallocation, the space a given FAM has might even change during the lifetime of the host struct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.