PedroA
PedroA

Reputation: 1925

Does malloc() reuse the same memory address in loop?

I'm starting to learn C programming and stumbled upon a situation that shows my lack of understanding of how memory allocation with malloc() works.

In a loop like the following:

// will only know `xlen` at runtime so I guess I have to use malloc here
char *x = malloc((xlen + 1) * sizeof(*x));
assert(x != NULL);  // memory allocation failed

while (fgets(line, sizeof(line), fp) != NULL) {
    strncpy(x, line, xlen);  // copy first xlen characters of line
    x[xlen] = '\0';          // ensure null terminating string

    // do something with x
    // x can now be "reused" for another line/loop iteration

    // free(x) - errors out with "pointer being freed was not allocated"
}
fclose(fp)
free(x);

If the statement free(x) is called inside the loop then when I run this program I get an error message something like a.out(37575,0x7fff964ce3c0) malloc: *** error for object 0x7fab47c02630: pointer being freed was not allocated.

Why am I seeing this error message and what is causing it?

Will the memory address block of x be "re-used" at each iteration of the loop? (I'd say yes, and that would actually be what I wanted in this case) In this case, is it safe to only free the allocated memory of x outside the scope of the loop?

Upvotes: 4

Views: 2603

Answers (4)

Richard Chambers
Richard Chambers

Reputation: 17573

The malloc() function provides a pointer to an area of memory that can then be used just like any pointer to a memory location.

The way to think of malloc() and free() is to consider it in terms of who owns a memory area. The malloc() function owns the memory that it is managing. When you call the malloc() function, or calloc() function, the function takes some of the memory it is managing and transfers ownership to you. When the function is called and the memory allocated there is a transfer of ownership from the memory management system of malloc() to you and at that point you own that memory and it is up to you to manage it. When the free() function is called to release the memory, you are transferring ownership of the memory area from you back to the memory management system of malloc().

Whenever a transfer of ownership is done, the entity that is giving up ownership is not supposed to do anything further with the memory area since it no longer owns it.

So when you do the malloc() you now own the memory and can use and reuse the memory area as much or as little as you want until such time that you call the free() function to give it back to malloc().

A pointer to a memory region given by malloc() is like any other pointer so far as using it is concerned.

Once you have transferred ownership of a memory area back to malloc() by using thee free() function, you can't call free() again with the same pointer without introducing an error. Once a memory area has been returned using free() you no longer have ownership of the memory so you shouldn't try to do anything else with it and that includes calling free() with the pointer again.

How malloc() manages memory is an implementation detail that will vary depending on the compiler and the C Runtime used. In some cases if you malloc() memory then free() the memory and then malloc() the same amount of memory again you may get lucky and get the same memory area again however it is not something you can count on or should expect.

malloc() provides you a pointer to a memory area that has a couple of guarantees. First of all the memory area provided will be at least as large as you requested. It may be larger but it won't be smaller than what you requested. Secondly the address provided will be on a memory boundary suitable for the machine architecture so that you can use the address provided for any variable type whether a built in such as int or double or a struct or an array of some type.

As the owner of the memory area provided by malloc() you are responsible for giving the memory area back to the malloc() functionality once you are done with it.

Your other primary responsibility is to respect the size of the memory area and to not write a larger data block into the area than the size requested when the malloc() was done. You are guaranteed only the amount of memory requested and writing a larger block of data into the memory will probably overwrite memory that you do not own but is owned by something else.

Unfortunately because the malloc() functionality, at least the optimized or non-debug version of the library, is designed with little as possible overhead, the functionality has few consistency and sanity checks in place. The malloc() functionality trusts that you will obey the rules and guidelines and only use what you own. And often when you break the rules and guidelines, you won't see the effect at the point where you have made a mistake but rather at some other point when some mysterious memory corruption is discovered and your program crashes.

Upvotes: 5

ryyker
ryyker

Reputation: 23208

You can continue to reusing memory (eg buf created by char *buf = calloc(sizeOfBuf); ) allocated to you on a single call to calloc(), until free(buf) is called, just as if buf was created statically, eg char buf[sizeOfBuf]; = 0. However, if the size of the buffer initially created by using calloc() needs to change, realloc() is available to do this, and is the preferable method. There are some caveats about using realloc however. Here is an example of using realloc, this one packaged into a function that resizes an existing dynamically allocated buffer, and takes care of some of the caveats:

realloc usage example:

// initial call to calloc
char *buf = calloc(sizeOfBuf);
if(buf)
{
    while(someConditionIsTrue)
    {
        // read new content
        //...
        //new content needs to be added to buf
        char *tmp = realloc(buf, newSizeOfBuffer);
        if(!tmp)//if failed...
        {
            free(buf);//... free original buf to prevent memory loss
            return NULL;// return null, caller must test for this
        }
        buf = tmp;//...else copy new mem location back to original pointer
        //continue doing stuff with buf
    }

    //when done with buf, free it
    free(buf);
}

One other suggestion, consider calloc() returns uninitialized memory, i.e. you own a space that contains whatever was occupying it when it was given to you. It is a good idea to follow that command with a method to clean up the buffer:

memset(buf, 0, sizeOfBuf);

Or use calloc().

Upvotes: 1

machine_1
machine_1

Reputation: 4454

You can use and reuse dynamically allocated memory as long as you have not called free() on it. If you free it and attempt to use it again, then you will have undefined behaviour.

Upvotes: 2

Blaze
Blaze

Reputation: 16876

free(x); frees the memory allocated by char *x = malloc(.... It frees all of the memory, and you don't have to worry about how much memory that was, as it keeps track of that. You just have to call free(x); once as you correctly do. This is why you get the error if you free it inside the loop.

Does it mean that the memory address block of x will be "re-used" at each iteration? (I'd say yes, and that would actually be what I wanted in this case)

Yes, you use the same memory. It overwrites the memory every time.

In this case, is it safe to only free the allocated memory of x outside the loop scope?

Yes, you have to free it outside of the loop. Because if you free it, then all of it is freed. If you did that inside the loop, then it would be undefined behavior to keep accessing that memory in following loop iterations.

Upvotes: 4

Related Questions