C: malloc() doesn't just allocate memory

  • Thread starter nobahar
  • Start date
  • #1
495
2
Hello,

Another (probably obvious) question:
malloc() assigns memory space in bytes; e.g. malloc(100) assigns 100 bytes of space and returns a pointer.

However, in creating a linked list, I can allocate space a assign it to a pointer and the structure already exists?
For example:
Code:
//Create structure
struct happy
{
     int x;
     struct happy *h_ptr;
};

int main( void )
{
     //Pointers for assigning head to link to first structure and a pointer for creating new links
     struct happy *head = NULL;
     struct happy *newlink = NULL;
     
     //Assign memory for structure and assign address of space to head pointer
     head = malloc(sizeof(struct happy));

     //Make the pointer in happy, h_ptr, point to NULL, since it is the last (and only) element
     head->h_ptr = NULL;

     .....
     return 0;
}

If malloc() only assigned space in memory, then why does head->h_ptr = NULL work? malloc() set aside the correct amount of space, but it doesn't "know" what for, the structure type hasn't been placed there. Does the pointer type - that malloc is set to point to a structure of type happy - mean that the structure type is created because of the pointer? I have seen typecasts, e.g. head = (struct happy *)malloc(sizeof(struct happy)), used, but also read that they are not necessary in C. If the typecast was used, I can imagine that it tells the malloc() function to "divide up" the space into a happy structure type; does the pointer type, by pointing to a happy structure type, do the same thing?

Any help appreciated,
Thanks in advance.
 

Answers and Replies

  • #2
D H
Staff Emeritus
Science Advisor
Insights Author
15,415
686
malloc returns a void* pointer. A type cast is not needed because void* converts to any pointer type without a cast -- in C, that is. This is not the case in C++ (but you should never use malloc in C++).

It doesn't hurt to add the cast; it doesn't cost anything. A typecast results in zero assembly code when converting from one pointer type to another.
 
  • #3
Borek
Mentor
28,777
3,250
In a way the structure of happy is "always there".

Pointer is just an address of a place in memory. Structure definition tells your program how to treat whatever data happens to be at the address variable sits in memory. You don't have to do anything for the structure to "exist". However, you have to initialize it with a correct data if you want the structure to make sense in your program. Memory allocated by malloc() can contain anything, and the content may even accidentally make sense.

Note: depending on the complier and mode it is working in, allocated memory can be either completely random, or initialized with some values (like 0xCDCDCDCD). This often makes debugging easier, but it sometimes means release version of the code behaves differently than the debug version (which usually means you forgot to initialize something in your code, and the debug version worked correctly by accident).
 
  • #4
495
2
A type cast is not needed because void* converts to any pointer type without a cast

Is it in any way accurate to understand this to mean something like the following:

I have my structure happy as before.

I declare a pointer that points to a structure of type happy.
Code:
struct happy *h_ptr = NULL;

When I assign space in memory using malloc() it would simply set aside that space, but by assigning the space to a pointer which is "set-up" to point to a happy-type structure, the space is "partitioned" to match the structure and its members: happy has members that perform specific functions, e.g. a pointer, this means the space in memory for this pointer has to be known to be separate from the other member, int x, and to be of type pointer. Its not simply setting aside, say, 12 bytes, but 4 for int and 8 for the pointer. I can immediately use the memory set aside as though this partition exists:
Code:
head = malloc(16);
head->h_ptr = NULL;
If on the other-hand I declared malloc(16), and assigned the return value to a char * pointer, it would set-aside the correct space for a string, and
Code:
head->h_ptr = NULL;
would not work.

I hope this makes sense; if so, is it in anyway accurate?
Thanks borek and DH for the help so far.
 
  • #5
Borek
Mentor
28,777
3,250
by assigning the space to a pointer which is "set-up" to point to a happy-type structure, the space is "partitioned" to match the structure and its members

No, it is not. That's what I tried to tell you. "Space partitioning" is defined by the structure definition. malloc doesn't care about internal structure, all it does is it allocates sizeof(happy) bytes. It will do exactly the same operation for every other structure that happens to occupy exactly the same number of bytes.

If on the other-hand I declared malloc(16), and assigned the return value to a char * pointer, it would set-aside the correct space for a string, and
Code:
head->h_ptr = NULL;
would not work.

You can easily make it work by casting a pointer. There is no such thing as a "correct space for string", there are 16 bytes of memory.
 
  • #6
6,054
391
the space is "partitioned" to match the structure and its members

No operation needs to be done when the program is running for this "partitioning".

happy has members that perform specific functions, e.g. a pointer, this means the space in memory for this pointer has to be known to be separate from the other member, int x, and to be of type pointer. Its not simply setting aside, say, 12 bytes, but 4 for int and 8 for the pointer.

That is correct, and that is known to the compiler when the program is compiled. A structure is a contiguous block of memory starting at some address x. The compiler knows that field a is at a fixed offset from the beginning of the structure, and it knows that field b is at another fixed offset from the beginning of the structure. So your expression "x->a" is compiled to something like "take address from variable x and add offset_a bytes to it; this is the address where the field is". Likewise, "x->b" is compiled to "take address from x and add offset_b bytes to it; this is the address where the field is". offset_a and offset_b are compile-time constants (you could even obtain them with some creative use of C).

The point of that is NOTHING needs to be done to the memory allocated by malloc() to use it as a structure.

This is markedly different in C++. In C++, something MAY need to be done; this is why malloc() is deprecated in C++ and operator new should be used whenever possible.
 
  • #7
rcgldr
Homework Helper
8,768
565
The point of that is NOTHING needs to be done to the memory allocated by malloc() to use it as a structure. This is markedly different in C++. In C++, something MAY need to be done; this is why malloc() is deprecated in C++ and operator new should be used whenever possible.
Whether something needs to be done or not normally depends on the structure (or class), not if it's C or C++. In this case, a pointer within the structure needs to be initialized to NULL. In C++, this is done via "constructor" code for the struct or class, in C, it needs to be done "manually" in code every time a new instance of a struct is allocated, and a function to allocate and initialized a struct could be used if wanted.
 
  • #8
6,054
391
Whether something needs to be done or not normally depends on the structure (or class), not if it's C or C++.

In C, a structure needn't be initialized to be used. That's not part of the language spec.

In C++, non-POD classes need initialization, which is part of the language spec.

In this case, a pointer within the structure needs to be initialized to NULL.

No. This is up to the programmer. It makes sense, ordinarily, but that is not a requirement.

In C++, this is done via "constructor" code for the struct or class

Even in C++ it is perfectly fine not to initialize members which are POD.
 
  • #9
495
2
You can easily make it work by casting a pointer. There is no such thing as a "correct space for string", there are 16 bytes of memory.

I should have tried this earlier; I didn't have the confidence because I wouldn't be happy that what I did was a correct method to test anything! I decided to assign the address returned from malloc() to a char * pointer and then assign the address from the char * pointer to the struct pointer and then proceed from there, accessing the structure's pointer. I think this is a correct test.

No operation needs to be done when the program is running for this "partitioning".



That is correct, and that is known to the compiler when the program is compiled. A structure is a contiguous block of memory starting at some address x. The compiler knows that field a is at a fixed offset from the beginning of the structure, and it knows that field b is at another fixed offset from the beginning of the structure. So your expression "x->a" is compiled to something like "take address from variable x and add offset_a bytes to it; this is the address where the field is". Likewise, "x->b" is compiled to "take address from x and add offset_b bytes to it; this is the address where the field is". offset_a and offset_b are compile-time constants (you could even obtain them with some creative use of C).

The point of that is NOTHING needs to be done to the memory allocated by malloc() to use it as a structure.

This is markedly different in C++. In C++, something MAY need to be done; this is why malloc() is deprecated in C++ and operator new should be used whenever possible.

Thanks voko, I appreciate Borek's response - it certainly helped - but yours is what made it "click"! Many thanks!
 

Related Threads on C: malloc() doesn't just allocate memory

Replies
12
Views
1K
  • Last Post
Replies
6
Views
2K
  • Last Post
Replies
4
Views
2K
  • Last Post
Replies
6
Views
2K
  • Last Post
Replies
7
Views
5K
Replies
5
Views
2K
Replies
3
Views
5K
  • Last Post
Replies
8
Views
3K
  • Last Post
Replies
17
Views
4K
Top