Does printf() allocate memory in C?
This simple method just creates an array of dynamic size n and initializes it with values 0 ... n-1. It contains a mistake, malloc() allocates just n instead of sizeof(int) * n bytes:
int *make_array(size_t n) {
int *result = malloc(n);
for (int i = 0; i < n; ++i) {
//printf("%d", i);
result[i] = i;
}
return result;
}
int main() {
int *result = make_array(8);
for (int i = 0; i < 8; ++i) {
printf("%d ", result[i]);
}
free(result);
}
When you check the output you will see that it will print some numbers as expected but the last ones are gibberish. However, once I inserted the printf() inside the loop, the output was strangely correct, even tho the allocation was still wrong! Is there some kind of memory allocation associated with printf()?
Strictly, to answer the question in the title, the answer would be that it depends on the implementation. Some implementations might allocate memory, while others might not.
Though there are other problems inherent in your code, which I will elaborate on below.
Note: this was originally a series of comments I made on the question. I decided that it was too much for a comment, and moved them to this answer.
When you check the output you will see that it will print some numbers as expected but the last ones are gibberish.
I believe on systems using a segmented memory model, allocations are "rounded up" to a certain size. Ie if you allocate X bytes, your program will indeed own those X bytes, however, you'll also be able to (incorrectly) run past those X bytes for a while before the CPU notices that you're violating bounds and sends a SIGSEGV.
This is most likely why your program isn't crashing in your particular configuration. Note that the 8 bytes you allocated will only cover two ints on systems where sizeof (int)
is 4. The other 24 bytes needed for the other 6 ints do not belong to your array, so anything can write to that space, and when you read from that space, you are going to get garbage, if your program doesn't crash first, that is.
The number 6 is important. Remember it for later!
The magic part is that the resulting array will then have the correct numbers inside, the printf actually just prints each number another time. But this does change the array.
Note: The following is speculation, and I'm also assuming you're using glibc on a 64-bit system. I'm going to add this because I feel it might help you understand possible reasons why something might appear to work correctly, while actually being incorrect.
The reason it's "magically correct" most likely has to do with printf
receiving those numbers through va_args. printf
is probably populating the memory area just past the array's physical boundary (because vprintf is allocating memory to perform the "itoa" operation needed to print i
). In other words, those "correct" results are actually just garbage that "appears to be correct", but in reality, that's just what happens to be in RAM. If you try changing int
to long
while keeping the 8 byte allocation, your program will be more likely to crash because long
is longer than int
.
The glibc implementation of malloc has an optimization where it allocates a whole page from the kernel every time it runs out of heap. This makes it faster because rather than ask the kernel for more memory on every allocation, it can just grab available memory from the "pool" and make another "pool" when the first one fills up.
That said, like the stack, malloc's heap pointers, coming from a memory pool, tend to be contiguous (or at least very close together). Meaning that printf's calls to malloc will likely appear just after the 8 bytes you allocated for your int array. No matter how it works, though, the point is that no matter how "correct" the results may seem, they are actually just garbage and you're invoking undefined behavior, so there's no way of knowing what's going to happen, or whether the program will do something else under different circumstances, like crash or produce unexpected behavior.
So I tried running your program with and without the printf, and both times, the results were wrong.
# without printf
$ ./a.out
0 1 2 3 4 5 1041 0
For whatever reason, nothing interfered with the memory holding 2..5
. However, something interfered with the memory holding 6
and 7
. My guess is that this is vprintf's buffer used to create a string representation of the numbers. 1041
would be the text, and 0
would be the null terminator, '