In C, what happens to memory values after a pointer's value is changed?

Viewed 313

In the following code, I pass a pointer by value to a function I'm hoping to have concatenate N strings onto the original one.

In the way I've written it, I'm allocating new memory by working out how many bytes to add onto the original string length.

This new string is then populated and the pointer to that string is returned.

So, let's say the original pointer msg is 0x000001, and it points to the starting char of the string "Hello\0".

Then in the function, a new pointer strNew eventually points to "Hello world, poop\0", and has a value of 0x0000f5, where that new string's memory is located.

Then, as a return of the function the msg pointer's value is now 0x0000f5.

My question is, what happens to the memory located at 0x000001?? It contains bytes for "Hello\0", but there is no longer a pointer to it. Does it get garbage collected? Is it a problem? Should I overwrite the contents somehow with ' ' chars?

If not, how do I free it from within the strcatcat() function?

The idea is to not have to worry about having a character array large enough for the strings to begin with. Is this unreasonable?

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

char * strcatcat(char * orig, const char* strArgs, ...){
    
    va_list valist;
    
    unsigned long initStrLength = strlen(orig);
    
    // work out how much me to realloc
    unsigned long moreBytes = 0;
        
    va_start(valist, strArgs);
    const char* str = strArgs;
    while (str != NULL ) {
        moreBytes += strlen(str);
        str = va_arg(valist, const char *);
    }
    
    // define a new char pointer to populate, of defined size
    char * strNew = NULL;
    strNew = (char *) malloc((moreBytes + initStrLength+1));
    
    // copy the original string into the start
    strcpy(strNew, orig);
    
    //reset, then go through and concat into new string
    va_start(valist, strArgs);
    str = strArgs;
    while (str != NULL ) {
    
        strcat(strNew, str);
        
        str = va_arg(valist, const char *);
            
    }
    
    // close list
    va_end(valist);
        
    // return this pointer
    return strNew;
    
}

int main()
{
 
    char * msg = "Hello";
    msg = strcatcat(msg, " World, ", "poop", NULL);
    printf("%s\n", msg);
    return 0;
}

Edit: Thanks all, that has cleared it up. I'm used to higher level langs like PHP, C# etc and reading about pointer arithmetic this question popped up that I couldn't find an answer that wasn't focused on the pointer rather than the value of the pointer.

TLDR for people in the future - without the caller managing it, the example I gave would cause a memory leak. The pointer inside main would need copying so as to deallocate.

3 Answers

My question is, what happens to the memory located at 0x000001?? It contains bytes for "Hello\0", but there is no longer a pointer to it.

Nothing happens to it... A pointer is nothing more than the address of some memory, so changing value of a pointer only changes which bytes it points to, it doesn't affect the value stored in the bytes pointed to.

Does it get garbage collected?

C doesn't have a built-in garbage collector; you're responsible for managing memory.

Is it a problem?

Maybe... it depends on the situation. In the case of your strcatcat() function, the pointer orig is passed, as you observed, by value. That means that the function gets its own copy of the pointer, and if the function changes the value of orig the caller's copy of the pointer doesn't change at all. Since strcatcat() didn't allocate the memory that orig points to, it's not responsible for freeing it... the caller (or whoever allocated or is otherwise responsible for that block) should do that.

It's certainly a problem if your program allocates blocks of memory and never frees them, so overall you should have a clear strategy for memory management. But unless you're very clear that calling strcatcat() will deallocate the block that's passed in, that function shouldn't touch the original block.

Should I overwrite the contents somehow with ' ' chars?

Why would you do that? Isn't it possible that the caller might want to use the original block for other things even after calling your function?

Bytes don't stop existing when you deallocate them; they're always there. Allocation is just the process of reserving a given range of bytes for a specific purpose, so that some other piece of code doesn't try to use the same bytes at the same time. When you free memory, the bytes remain there, but the block that you free becomes available for other uses. It does make sense to write over data before freeing a block if the data is somehow sensitive, e.g. a password or some sort of personal information. But as long as you don't need to protect the data in a block, there's no need to clear the memory before freeing it.

My question is, what happens to the memory located at 0x000001?? It contains bytes for "Hello\0", but there is no longer a pointer to it. Does it get garbage collected? Is it a problem? Should I overwrite the contents somehow with ' ' chars?

For starters in C there is no garbage collector. And secondly you may not change a string literal. Any attempt to change a string literal results in undefined behavior.

In this declaration

char * msg = "Hello";

there is declared a pointer to the first character of the string literal "Hello". The string literal itself has the static storage duration and will be alive until the program will finish its execution independent on whether the value of the pointer msg was changed or not.

You may imagine this the following way

char unnamed_string_literal[] = { 'H', 'e', 'l', 'l', 'o', '\0' };

int main( void )
{
    char *msg = unnamed_string_literal;
    //...
}

You should not bother about string literals.

For example consider the following valid program.

#include <stdio.h>

int main(void) 
{
    char *msg = "Hello";
    
    printf( "%s ", msg );
    
    msg = "World!";
    
    puts( msg );
    
    return 0;
}

The program output is

Hello World!

In essence this program is similar (except some unimportant details in the context of the question) to the following program

#include <stdio.h>

char word1[] = { 'H', 'e', 'l', 'l', 'o', '\0' };
char word2[] = { 'W', 'o', 'r', 'l', 'd', '!', '\0' };

int main(void) 
{
    char *msg = word1;
    
    printf( "%s ", msg );
    
    msg = word2;
    
    puts( msg );
    
    return 0;
}

First of all if you dynamically allocate memory for "Hello\0" and you not free it, you will create a memory leak. It's a school example how to make one.

Which leads to the next realization: There is no garbage collector in C unless you create one. You are responsible for EVERYTHING. That's the beauty of C, because you also control everything. You have the power to decide for the best way for your particular problem.

Next issue is that from the inside of a function you never know how the memory was allocated. I can create char example[100] = "My value"; at the caller level and it is allocated on the stack. If you try to free it from inside of your function the program will fail.

There are few basic approaches to this problem:

  • The caller provides a buffer, the function fails if the buffer is not large enough
  • The function allocates the memory and does not touch inputs, the caller is responsible for everything it receives.
  • A higher level string abstraction (I would say a class in C++, but let's say some type with some related actions) One example may be Glib String https://developer.gnome.org/glib/stable/glib-Strings.html , but there are many other implementations.

Each of them have its pluses and minuses (preventing leaks, memory fragmentation etc.). This is on the C programmer to decide which approach works the best for your case.

Related