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

int main(void)
{
        char* a = malloc(5 * sizeof(char));
        a = "1";
        free(a);
}

I have two questions regarding the above code:

  1. Why does the code gives an error with free(a) when I initialize a like this :

a = "1"

and doesn't give any error with free(a) when I initialize a like this :

a[0] = '1'.

  1. When I initialize a with a[0] = '1' and print the string a using printf("%s", a) why do I get the result

    '1'

without declaring the second index with a '\0'?
Arnab Nandy
  • 6,472
  • 5
  • 44
  • 50
Lokesh
  • 2,842
  • 7
  • 32
  • 47
  • 2
    a = "1"; doesn't fill a with a, it reassigns the pointer to some string "a" in memory. That string is compiled and fixed and can't/shouldn't be freed. – Pieter21 Sep 17 '14 at 10:35
  • Quite literally, anytime a C program does this: `Type* p = malloc(...); p = ...` its wrong. Your program leaks memory in the first two lines, then free's the address of a read-only string literal.. – WhozCraig Sep 17 '14 at 10:36
  • 3
    About the second index with '\0'. That's pure luck, because memory contained '\0'. – Pieter21 Sep 17 '14 at 10:38

5 Answers5

11

The rules with malloc and free are that you must only pass to free a pointer that was allocated by a call to malloc (or one of its equivalents like realloc or calloc). You do not do that:

char* a = malloc(5 * sizeof(char));
a = "1";
free(a);

When you call free, you pass a which was not allocated by a call to malloc. This results in your program having undefined behaviour.


The problem in your understanding is that I suspect you don't understand what a = "1" does.

I suspect that you imagine this to copy the string in to the memory that you allocated it does not. It changes the pointer a to point to a different address.

Indeed, by assigning to a, rather than the memory that it points to, you are leaking the allocated memory. Your code is exactly analogous to this:

int i = 1;
i = 2;

I'm sure you can see that the initialization of i is pointless because you immediately overwrite the value of i with a new value. That's exactly what you did with your pointer.


You can copy the string with strcpy. Like this:

char* a = malloc(5 * sizeof(char));
strcpy(a, "1");
free(a);

Similarly, when you wrote

char* a = malloc(5 * sizeof(char));
a[0] = '1';
printf("%s", a);
free(a);

you were assigning the contents of the buffer allocated by your call to malloc. And hence the call to free is correct.

However, the string is not necessarily null-terminated when you do that. You observed that your printf appeared to work but that was just by chance.

The contents of a[1] are ill-defined and it seems that, by chance, a[1] did contain a null when you ran your program.

This is why you typically use a string function like strcpy to perform copies and ensure null-termination. You do of course need to take care not to overrun your buffer.


Finally, by definition, sizeof(char) == 1 so you can write the malloc like this:

char* a = malloc(5);
Bhavesh Odedra
  • 10,990
  • 12
  • 33
  • 58
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • When working with strings, it's a good idea to always use the functions in `string.h` like `strcpy` in this answer. Unless you really _do_ know what you are doing, it's less likely to break or give you an unwanted result, and it is likely more efficient than a home-grown function too. – Baldrickk Sep 17 '14 at 10:48
4

a = "1"; is assigning read-only memory to a. a now points to a completely different memory location. The previous return of malloc is now lost so you will not be able to free the memory.

This is a disaster since, not including the memory leak, you have undefined behaviour when attempting to free the new value of a. Boom. Futhermore, if you attempt to modify the contents of a then that's undefined behaviour too. Boom.

Without the errant reassignment, a[0] = '1' is perfectly fine: you're deferencing the zeroth element of the allocated character buffer and setting its value to literal 1.

But if you attempt printf("%s", a) on this then, boom! More undefined behaviour since you haven't null-terminated the character buffer which printf requires you to do in order to prevent it from accessing memory that you don't own. To to that, use a[1] = 0.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
2

When you do

a = "1";

a no longer points to the memory you allocated with malloc. Rather, it points to a string constant, stored in the text segment of your program. Naturally, such a pointer can't be freed. The original a pointer is lost.

As for your second question, that is undefined behavior. a[1] happened to be a null character by sheer dumb luck, but you should of course not rely on this happening.

Imre Kerr
  • 2,388
  • 14
  • 34
1
a = "1";

makes a point to a string literal, which is stored in read-only memory. You can not free() that part of memory.

a[0] = '1'; makes it to store a value of 31 (since that's the hex ascii value of 1) in the first byte that a points to. a still points to address that malloc had returned, therefore freeing it is legal operation. a[0] is the same as *(a + 0), so you see that address of a is untouched, contents where a points to are changed.

Other answers already have the information about null terminating your char array. You should know that printf() stops writing values to stdout when it encouters a null terminator. If there's '1' at 0th position and no '\0' at 1st position of your array, you might see some garbage values.


You should know that "1" and '1' are two different things.

macfij
  • 3,093
  • 1
  • 19
  • 24
-2

Corrected program with inline comments for your understanding:

int main(void)
{
  char* a = (char *) malloc(5 * sizeof(char)); // prefer casting from void * to char *
  // a = "1"; -- wrong conversion from string constant to char *
  strcpy(a, "1"); // this would work
  free(a);
}
Dr. Debasish Jana
  • 6,980
  • 4
  • 30
  • 69
  • 5
    Permission to remove the `malloc` cast? – Bathsheba Sep 17 '14 at 10:34
  • Nope, then you should use strdup, but also free before strdup first. – Pieter21 Sep 17 '14 at 10:36
  • 1
    This is a C question. You should not cast the result of `malloc()` in C. The cast is not needed and may hide useful compiler warnings. – fuz Sep 17 '14 at 10:58
  • LHS is char *, rhs is void *, what's the harm in casting? It is good programming practice – Dr. Debasish Jana Sep 17 '14 at 11:01
  • 1
    Simple, lhs is char *, that's why? Can you cast to anything else? Come on! – Dr. Debasish Jana Sep 17 '14 at 11:54
  • C allows implicit casting from/to `void *`. – Mohit Jain Sep 17 '14 at 12:29
  • Yes, but what's the harm in explicit casting? Downvoting because of being explicit? – Dr. Debasish Jana Sep 17 '14 at 12:48
  • http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc http://stackoverflow.com/questions/1565496/specifically-whats-dangerous-about-casting-the-result-of-malloc I expect that you received downvotes for the original version of the answer which incorrectly stated *need casting from `void*` to `char*`* – David Heffernan Sep 17 '14 at 12:49
  • Often the declaration of a pointer like `char *a` and subsequent assignments, like `a = malloc(...)` are lines apart. Using the style of `a = malloc(n * sizeof *a);` is easier to _maintain_. Should the type of pointer change, something like `a = (char *) malloc(5 * sizeof(char));` get coded, not only does the original type change, every allocation for `a` needs 2 edits. Simpler and less error prone to use `a = malloc(n * sizeof *a);`. – chux - Reinstate Monica Sep 17 '14 at 15:53
  • That's why C++11 has come up with auto. Type checking is so weak in C! – Dr. Debasish Jana Sep 18 '14 at 03:01