Home > Enterprise >  How is memory allocation done for char* variables? Does it reallocate itself if further used?
How is memory allocation done for char* variables? Does it reallocate itself if further used?

Time:07-23

So I have this code,

int main() {
char* currentWorkingDirectory = getcwd(NULL, 0);
char* moviesFileDir = currentWorkingDirectory;
char* buyersFileDir = currentWorkingDirectory;
strcat(moviesFileDir, "\\Movies.txt");
strcat(buyersFileDir, "dljasdoihjawuieyawodiujasiodyhawoirasioufgyaisdufghytewdasasasasasasasasasasasasasasasasasasasasasasasasasasasaaaaaaaaaaaaaaaaaaaaaaa");
printf("Directory: %s\n", moviesFileDir);
printf("Directory: %s\n", buyersFileDir);
return 0; }

And was wondering how exactly is the memory allocated for these two variables? (moviesFileDir, buyersFileDir), because for example if I do not instantiate them with the value of currentWorkingDirectory I will have to use malloc to allocate a memory size for the variables, but in this case they already have space alocated since they are a copy of currentWorkingDirectory, but from that point onwards, for example when I did the strcat cast on them, how did they know how to allocate enough memory for whatever it was concatenated with? If I'm not mistaken, if I would've used malloc from the start and tried to do the same strcat on them I would've had to use realloc on them with the size of the concatenated string, right?

Sorry if I'm not concise enough but this is my first stackoverflow topic xD

CodePudding user response:

Quoth the documentation for strcat:

The behavior is undefined if the destination array is not large enough for the contents of both src and dest and the terminating null character.

In other words, strcat does not allocate any memory by itself.

The documentation for getcwd says

As an extension to the POSIX.1-2001 standard, glibc's getcwd() allocates the buffer dynamically using malloc(3) if buf is NULL. In this case, the allocated buffer has the length size unless size is zero, when buf is allocated as big as necessary. The caller should free(3) the returned buffer.

In other words, we should assume currentWorkingDirectory is exactly as large as it needs to be and none larger, so strcating into it is Undefined Behavior and will likely scribble onto other memory.

That will either corrupt your program's memory or outright crash it right away (the former case being more insidious and harder to trace).

You should also notice that char* moviesFileDir = currentWorkingDirectory; etc. do not take copies.

Here on Godbolt, your program happily crashes with

malloc(): corrupted top size

because you've written onto memory you shouldn't have written onto (evidently memory containing malloc's internal state).


A safe but somewhat non-efficient implementation that allocates just enough memory (though this example leaks memory since we're not free()ing anything we allocate, either directly or indirectly) would be

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
  char *currentWorkingDirectory = getcwd(NULL, 0);
  int string_length = snprintf(NULL, 0, "%s%s%s", currentWorkingDirectory, "\\Movies.txt", "dljasdoihjawuieyawodiujasio");
  char *buf = malloc(string_length   1);
  sprintf(buf, "%s%s%s", currentWorkingDirectory, "\\Movies.txt", "dljasdoihjawuieyawodiujasio");
  printf("Directory: %s\n", buf);
  return 0;
}

which uses snprintf() to first compute how many bytes are required, then allocates that (plus 1 for the trailing NUL), then sprintf()s into that new buffer.

Finally, here's a reusable implementation of the above:

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

/**
 * Like sprintf, but allocates just enough memory for the string to format.
 * 
 * The caller must free the returned buffer (which could be NULL if there's a problem).
 */
char *sprintf_alloc(const char *restrict format, ...) {
    char *buf = NULL;
    va_list va;
    // (1) Figure out how much memory to allocate.
    va_start(va, format);
    int string_length = vsnprintf(NULL, 0, format, va);
    va_end(va);
    if(string_length < 0) return NULL;  // format error?
    // (2) Allocate that much memory.
    buf = malloc(string_length   1);
    if(buf == NULL) return NULL; // allocation error?
    // (3) Actually format into the buffer.
    va_start(va, format);
    vsprintf(buf, format, va);
    va_end(va);
    return buf;
}

int main() {
  char *currentWorkingDirectory = getcwd(NULL, 0);
  char *buf = sprintf_alloc("%s%s%s", currentWorkingDirectory, "\\Movies.txt", "dljasdoihjawuieyawodiujasio");
  free(currentWorkingDirectory);
  printf("Directory: %s\n", buf);
  return 0;
}
  •  Tags:  
  • c
  • Related