First of all, I'm sorry for my bad English.
I have a question about 'realloc' function in language C.
I thought it allocates memories with given length.
When the memories ain't enough for the given length, it copies whole memories to another memory pointer and returns it.
I wanted to use realloc function with preserving the old memories.
So I sentenced another pointer and made it pointing at the old memories.
But whenever 'realloc' function works, it frees the old memories.
How can I preserve old memories?
In addition, I want to know why this is happenend.
the below is code and its result I've used for testing realloc function.
#include <stdio.h>
#include <stdlib.h>
int* ptr1, lenPtr1;
int* ptr2;
void double_ptr1(){
lenPtr1 *= 2;
ptr1 = (int*)realloc(ptr1, sizeof(int) * lenPtr1);
}
void print(){
printf("ptr1 -> %p, *ptr1 = %d\n", ptr1, *ptr1);
printf("ptr2 -> %p, *ptr2 = %d\n", ptr2, *ptr2);
printf("\n");
}
int main(){
lenPtr1 = 10;
ptr1 = (int*)malloc(sizeof(int) * lenPtr1);
ptr2 = ptr1;
*ptr1 = 10;
print();
double_ptr1();
print();
system("pause");
return 0;
}
ptr1 -> 0108D2F0, *ptr1 = 10
ptr2 -> 0108D2F0, *ptr2 = 10
ptr1 -> 00FF5740, *ptr1 = 10
ptr2 -> 0108D2F0, *ptr2 = -17891602
An example incorporating all improvements suggested in the comments and avoiding the use of global variables, and freeing all allocated memory before the program exits. There is only a need to hold the terminal window open on windows, so conditionally enclose the system("pause");.
Putting it altogether, you would have:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int *double_ptr (int *ptr, int *ptrsz) /* pass needed information as parameters */
{
int *newptr = malloc (2 * *ptrsz * sizeof *newptr); /* allocate new block of mem */
if (newptr) { /* validate allocation */
memcpy (newptr, ptr, *ptrsz * sizeof *ptr); /* copy to new block of mem */
*ptrsz *= 2; /* update allocated size */
}
return newptr; /* return pointer */
}
void print (int *ptr1, int *ptr2) /* pass needed information as parameters */
{
/* only one call to printf required */
printf ("ptr1 -> %p, *ptr1 = %d\nptr2 -> %p, *ptr2 = %d\n\n",
(void*)ptr1, *ptr1, (void*)ptr2, *ptr2);
}
int main (void) {
int *ptr1 = NULL, *ptr2 = NULL, lenPtr1 = 10; /* avoid global variables */
if (!(ptr1 = malloc (lenPtr1 * sizeof *ptr1))) { /* validate EVERY allocation */
perror ("malloc-ptr");
return 1;
}
ptr2 = ptr1; /* pointer 1 and 2 hold same address where 10 is stored in memory */
*ptr1 = 10;
printf ("lenPtr1: %d\n", lenPtr1); /* output len, addresses, values */
print (ptr1, ptr2);
if (!(ptr1 = double_ptr (ptr1, &lenPtr1))) { /* double size of ptr1 */
perror ("malloc-double-ptr1");
return 1;
}
printf ("lenPtr1: %d\n", lenPtr1); /* output len, addresses, values */
print (ptr1, ptr2);
free (ptr1); /* free allcoated memory */
free (ptr2);
#if defined (_WIN32) || defined (_WIN64)
system("pause");
#endif
}
Example Use/Output
$ ./bin/doubleptrsz
lenPtr1: 10
ptr1 -> 0xb18260, *ptr1 = 10
ptr2 -> 0xb18260, *ptr2 = 10
lenPtr1: 20
ptr1 -> 0xb186a0, *ptr1 = 10
ptr2 -> 0xb18260, *ptr2 = 10
Let me know if you have further questions.
When you realloc the original pointer, which is pointed by ptr2, the address of the original pointer is removed.
So after you realloc the ptr1, ptr2 points address that does not exists anymore.
If you want to preserve old memory, then you just need to malloc(or calloc) new memory to ptr1 with no realloc original one.
When the memories ain't enough for the given length, it copies whole memories to another memory pointer and returns it.
Not quite. realloc always deallocates the old memory. It may return the same pointer.
From C11 7.20.3.4 realloc...
The realloc function deallocates the old object pointed to by ptr and returns a pointer to a new object that has the size specified by size.... The realloc function returns a pointer to the new object (which may have the same value as a pointer to the old object)
I wanted to use realloc function with preserving the old memories.
You can't.
If you need more memory, malloc a new hunk of memory instead of reallocing the old one.
If you want to shrink memory, continue to use the memory as is. It'll work fine, it's just a waste of memory.
I'd recommend neither. Instead, redesign whatever you're doing so the pointer is accessed indirectly. Perhaps using a pointer to a pointer. Or put the pointer inside a struct and pass around a pointer to the struct.
Alternatively, redesign it so you do not need to realloc. For example, when reading from a file use a fixed size buffer to read the line and then copy that to memory with exactly enough space.
// A large buffer to reuse for each line.
char buf[BUFSIZ];
while( fgets(buf, sizeof(buf), fp) ) {
// Allocate exactly enough space for the line.
// In reality, use strdup.
char *string = malloc(strlen(buf) + 1);
strcpy(string, buf);
}
Or change to a data structure which does not rely on a single block of memory. For example, a linked list instead of an array.
In addition, I want to know why this is happened.
There are many implementations of malloc, but they all must allocate contiguous blocks of memory with no overlap.
Consider two calls to malloc.
char *foo = malloc(8);
char *bar = malloc(8);
And let's say foo and bar are allocated adjacent blocks of memory like so. (Note: there's no guarantee they will be adjacent and many reasons why they shouldn't be.)
0123456789abcdef0123456789abcdef
^^^^^^^^
| ^^^^^^^^
foo |
bar
Now you try to grow foo.
foo = realloc(foo, 16);
If realloc tries to keep the same pointer, foo would have to overlap into bar's memory.
0123456789abcdef0123456789abcdef
^^^^^^^^^^^^^^^^
| ^^^^^^^^
foo |
bar
That's not allowed, allocations cannot overlap. They also must be contiguous, so it cannot do this:
0123456789abcdef0123456789abcdef
^^^^^^^^ ^^^^^^^^
| ^^^^^^^^
foo |
bar
If it were allowed to do that, pointer math would not work. You would not be able to reliably add to foo to walk through its allocated memory.
So realloc must free foo and allocate a new block. (Again, no guarantee where it will be allocated.)
0123456789abcdef0123456789abcdef
^^^^^^^^
| ^^^^^^^^^^^^^^^^
bar |
foo
Related
When we allocating memory spaces for a string, do the following 2 ways give the same result?
char *s = "abc";
char *st1 = (char *)malloc(sizeof(char)*strlen(s));
char *st2 = (char *)malloc(sizeof(s));
In other words, does allocate the memory based on the size of its characters give the same result as allocating based on the size of the whole string?
If I do use the later method, is it still possible for me to add to that memory spaces character by character such as:
*st = 'a';
st++;
*st = 'b';
or do I have to add a whole string at once now?
Let's see if we can't get you straightened out on your question and on allocating (and reallocating) storage. To begin, when you declare:
char *s = "abc";
You have declared a pointer to char s and you have assigned the starting address for the String Literal "abc" to the pointer s. Whenever you attempt to use sizeof() on a_pointer, you get sizeof(a_pointer) which is typically 8-bytes on x86_64 (or 4-bytes on x86, etc..)
If you take sizeof("abc"); you are taking the size of a character array with size 4 (e.g. {'a', 'b', 'c', '\0'}), because a string literal is an array of char initialized to hold the string "abc" (including the nul-terminating character). Also note, that on virtually all systems, a string literal is created in read-only memory and cannot be modified, it is immutable.
If you want to allocate storage to hold a copy of the string "abc", you must allocate strlen("abc") + 1 characters (the +1 for the nul-terminating character '\0' -- which is simply ASCII 0, see ASCII Table & Description.
Whenever you allocate memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. So if you allocate for char *st = malloc (len + 1); characters, you do not want to iterate with the pointer st (e.g. no st++). Instead, declare a second pointer, char *p = st; and you are free to iterate with p.
Also, in C, there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?.
If you want to add to an allocation, you use realloc() which will create a new block of memory for you and copy your existing block to it. When using realloc(), you always reallocate using a temporary pointer (e.g. don't st = realloc (st, new_size);) because if when realloc() fails, it returns NULL and if you assign that to your pointer st, you have just lost the original pointer and created a memory leak. Instead, use a temporary pointer, e.g. void *tmp = realloc (st, new_size); then validate realloc() succeeds before assigning st = tmp;
Now, reading between the lines that is where you are going with your example, the following shows how that can be done, keeping track of the amount of memory allocated and the amount of memory used. Then when used == allocated, you reallocate more memory (and remembering to ensure you have +1 bytes available for the nul-terminating character.
A short example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define THISMANY 23
int main (void) {
char *s = "abc", *st, *p; /* string literal and pointer st */
size_t len = strlen(s), /* length of s */
allocated = len + 1, /* number of bytes in new block allocated */
used = 0; /* number of bytes in new block used */
st = malloc (allocated); /* allocate storage for copy of s */
p = st; /* pointer to allocate, preserve st */
if (!st) { /* validate EVERY allocation */
perror ("malloc-st");
return 1;
}
for (int i = 0; s[i]; i++) { /* copy s to new block of memory */
*p++ = s[i]; /* (could use strcpy) */
used++; /* advance counter */
}
*p = 0; /* nul-terminate copy */
for (size_t i = 0; i < THISMANY; i++) { /* loop THISMANY times */
if (used + 1 == allocated) { /* check if realloc needed (remember '\0') */
/* always realloc using temporary pointer */
void *tmp = realloc (st, 2 * allocated); /* realloc 2X current */
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-st");
break; /* don't exit, original st stil valid */
}
st = tmp; /* assign reallocated block to st */
allocated *= 2; /* update allocated amount */
}
*p++ = 'a' + used++; /* assign new char, increment used */
}
*p = 0; /* nul-terminate */
printf ("result st : %s\n" /* output final string, length, allocated */
"length st : %zu bytes\n"
"final size : %zu bytes\n", st, strlen(st), allocated);
free (st); /* don't forget to free what you have allocated */
}
Example Use/Output
$ ./bin/sizeofs
result st : abcdefghijklmnopqrstuvwxyz
length st : 26 bytes
final size : 32 bytes
Look things over and let me know if this answered your questions, and if not, leave a comment and I'm happy to help further.
If you are still shaky on what a pointer is, and would like more information, here are a few links that provide basic discussions of pointers that may help. Difference between char pp and (char) p? and Pointer to pointer of structs indexing out of bounds(?)... (ignore the titles, the answers discuss pointer basics)
This creates a pointer to the beginning of the array of two pointers.
void **p;
p = malloc(2 * sizeof(void *));
for (int i = 0; i < 2; ++i) {
p[i] = (void *)malloc(sizeof(void *));
}
This creates a pointer to the beginning of the array of two pointers.
void *c = *p;
And this should change the value of the first pointer in the array, right?
I want the first item in array points to my int.
int *a = (int *)malloc(sizeof(int));
*a = 5;
// c points to *p, the first item in array
// *c should be value of the first item in array then
*c = a;
But this doesn't work.
c should be same as *p(p[0]), so when I change c value, it should change *p(p[0]) value as well.
What am I missing?
I believe the point you are missing is that when you loop allocating p[i] = malloc (sizeof (void*)) you are replacing the addresses at both p[0] and p[1] -- which were already valid pointers to begin with.
Specifically, p = malloc(2 * sizeof(void *)); allocates a block of memory for 2-pointers and assigns the beginning address of that block to p. At this point, both p[0] and p[1] are valid (but Uninitialized) pointers. When you create c, it holds the same Uninitialized address of the first pointer in the allocated block assigned to p. At this point neither c or p[0] point anywhere and neither points at the other -- they just hold the same indeterminate address as their value.
When you assign c = a, now c holds a valid address, but p[0] remains Uninitialized. You must initialize p[0] in the same way before you can use the address it holds. There is no need to allocate further. For example:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
void **p = malloc (2 * sizeof *p), /* allocate storage for 2 pointers */
*c = *p; /* c holds the UNINITIALIZED/indeterminate address of the 1st pointer */
int *a = malloc (sizeof *a);
if (!p || !a) {
perror ("malloc-p_or_a");
return 1;
}
*a = 5; /* assign 5 to allocated int */
c = a; /* initialize address held by pointer c to a, p[0] still uninitialized */
*p = c; /* initialize address help by pointer p[0] to c */
printf ("\n a - value at %p : %d\n c - value at %p : %d\np[0] - value at %p : %d\n",
(void*)a, *a, c, *(int*)c, p[0], *(int*)p[0]);
...
As mentioned in the comment, if you are only storing single addresses at p[0] and p[1], then any further allocation is superfluous. You already have valid storage for a pointer you can reference at both p[0] and p[1]. That doesn't mean you can't allocated further, p itself still holds the beginning address for the original block allocated -- so it can still be freed (no memory leak), but you don't provide any additional storage by replacing the pointers at p[0] and p[1] with individually allocated pointers.
Now when you do allocate, you assign a new address as the values for both p[0] and p[1], e.g.
for (int i = 0; i < 2; i++) /* now loop initializeing p[i] */
if (!(p[i] = malloc (sizeof (void*)))) { /* assigns NEW address to p[i] */
perror ("malloc-p[i]");
return 0;
}
...
(In C, there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?)
Again, at this point, you must initialize the address stored in the block of memory with it's beginning address at p[0] and p[1], e.g.
int b = 10; /* new integer value */
p[0] = &b; /* assign address to pointer at p[0] */
c = p[0]; /* assign to pointer at c */
printf ("\n a - value at %p : %d\n c - value at %p : %d\np[0] - value at %p : %d\n",
(void*)a, *a, c, *(int*)c, p[0], *(int*)p[0]);
}
Example Use/Output
Above is a complete program that makes use of p[0] and p[1], both before and after your for loop allocations. If you compile and run it, you would receive:
$ ./bin/voidptr_int
a - value at 0x12a6280 : 5
c - value at 0x12a6280 : 5
p[0] - value at 0x12a6280 : 5
a - value at 0x12a6280 : 5
c - value at 0x7ffc47fdec6c : 10
p[0] - value at 0x7ffc47fdec6c : 10
It's an interesting exercise, but I believe where you were confused is after you declare void *c = *p; neither c or *p (which is the same as p[0]) point to each other -- they simply hold the same uninitialized/indeterminate address. The compounding things you allocation in the for loop changed the addresses held by both p[0] and p[1] so that whatever address was there before is overwritten with the address of the newly allocated pointers. I'm not sure which of the those two were causing the most confusion, but I suspect it was either one or the other.
c will get a copy of *p. After that, there is no connection between the two. Changing one will not change the other.
If you want to change p[0] thru c, c must be a pointer-to-pointer-to-void, void **c = p;. Then you can assign *c = a; to change p[0] (and leak the memory you allocated for p[0]).
I want the first item in array points to my int.
There are two cases (read x as 0 < x < N, where N is number of void * allocated to p) :
CASE I: p[x] pointers pointing to valid memory locations and use another pointer to make changes to the value of those memory locations.
CASE II: p[x] pointers not pointing to valid memory locations and use another pointer to make p[x] pointers pointing to some valid memory location.
Looks like you are confused between these two cases because you are allocating memory to p[x] pointers and then trying to assign some other memory location to *p (p[0]) using some other pointer c. With this, you end up leaking memory in your program because of not properly handling the allocated memory.
First I would like to point out couple of things in your code:
Here, you are allocating memory of void * size:
p[i] = (void *)malloc(sizeof(void *));
^^^^^^^^
If you want void pointers p[x] to point to memory location which hold an int
value, you should do:
p[i] = (void *)malloc(sizeof (int));
The type of c is void *, so *c type is void. Compiler must be throwing warning/error on this statement because void is an incomplete type and its not assignable:
*c = a;
From C11 Standard#6.2.5p19:
19 The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.
Now, lets discuss the CASE I and II in detail:
CASE I: p[x] pointers pointing to valid memory locations and use another pointer to make changes to the value of those memory locations.
void **p;
p = malloc(2 * sizeof(void *));
if (NULL == p) {
fprintf (stderr, "Failed to allocate memory");
exit(EXIT_FAILURE);
}
for (int i = 0; i < 2; ++i) {
p[i] = malloc(sizeof (int));
if (NULL == p[i]) {
fprintf (stderr, "Failed to allocate memory");
exit(EXIT_FAILURE);
}
}
Now, the void pointers p[x] pointing to memory locations of type int.
Assume, you want to assign value (or make changes) to p[0] memory location using some other pointer:
void *c = *p; // or void *c = p[0];
int a = 5;
*(int *)c = a; // need to type cast c to the type of memory it will point to
printf ("%d\n", *(int *)*p); // or printf ("%d\n", *(int *)p[0]);
Once done with pointers, make sure free the allocated memory. Here, you need to free p[x] pointers and then pointer p.
CASE II: p[x] pointers not pointing to valid memory locations and use another pointer to make p[x] pointers pointing to some valid memory locations.
In this case, you don't need to allocate memory to p[x] pointers.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
void **p;
p = malloc(2 * sizeof(void *));
if (NULL == p) {
fprintf (stderr, "Failed to allocate memory");
exit(EXIT_FAILURE);
}
void **c = p;
int *a = malloc(sizeof(int));
if (NULL == a) {
fprintf (stderr, "Failed to allocate memory");
exit(EXIT_FAILURE);
}
*a = 5;
*c = a;
printf ("p: %p\n", (void *)p);
printf ("c: %p\n", (void *)c);
printf ("a: %p\n", (void *)a);
printf ("*c: %p\n", (void *)*c);
printf ("*p: %p\n", (void *)*p);
printf ("%d\n", *(int *)*p);
free(a);
free(p);
return 0;
}
Output:
p: 0x7fe603c05710
c: 0x7fe603c05710
a: 0x7fe603c05720
*c: 0x7fe603c05720
*p: 0x7fe603c05720 // *p pointer is pointing to memory location allocated to pointer a
5
Note: Whether to type cast malloc result or not is opinion based. So, whatever you choose is up to you but make sure to be consistent everywhere in the code that you write.
When discussing pointers and arrays, the explanations often tell that initializing an array and then initializing a pointer to the same memory location does make you able to use that pointer in the same way as the first array:
int myIntArray[3] = {5, 6, 7};
int* ptr = myIntArray; // ptr[2] = myIntArray[2] = 7
I understand that this works as the allocation of memory on the stack is done with the first initialization int myIntArray[3]. But if one creates a pointer, it does not allocate potential memory for an array.
Thereby is my question, is it safe to create a pointer and use it as an array directly or might some other memory used be overwritten or such?
int* ptr;
*ptr = 5;
*(ptr+1) = 6;
*(ptr+2) = 7;
My guess is that if the address (ptr+2) would contain some previously allocated memory, such as an earlier initialized variable, the computer would reallocate ptr and (ptr+1) to some place were (ptr+2) is not used since before.
No, your example is not safe.
Your ptr variable is not explicitly initialized.
If ptr is a local variable, it will be initialized to a undefined (i.e. random) value. Thus, *ptr = 5 will write the value 5 to a random memory location. This will cause either a memory corruption or (more likely) a segmentation fault. See the following example:
#include <stdio.h>
int main(void)
{
int *ptr; /* Local variables are not intitialized and contain a random value */
printf("ptr contains address %p\n", ptr); /* Prints a random value */
ptr[0] = 123; /* BAD: On macOS causes memory corruption */
return 0;
}
See also note [1] below.
If ptr is a global variable, it will be initialized to zero. This *ptr = 5 will write the value 5 to virtual address 0, which most likely will cause a segmentation fault.
#include <stdio.h>
int *ptr; /* Global variables are initialized to zero */
int main(void)
{
printf("ptr contains address %p\n", ptr); /* Prints 0x0 */
ptr[0] = 123; /* BAD: On macOS causes "Segmentation fault: 11" */
return 0;
}
The same is true for static variables:
#include <stdio.h>
int main(void)
{
static int *ptr; /* Static variable are initialized to zero */
printf("ptr contains address %p\n", ptr); /* Prints 0x0 */
ptr[0] = 123; /* BAD: On macOS causes "Segmentation fault: 11" */
return 0;
}
If you want to use a pointer that does not point to an array, you should explicitly allocate the memory, for example by calling malloc
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *ptr; /* Local variables are not intitialized and contain a random value */
/* Explicitly allocate enough memory to store one int value */
ptr = malloc(sizeof(int));
if (!ptr) {
fprintf(stderr, "Out of memory");
exit(1);
}
printf("ptr contains address %p\n", ptr); /* Prints a random value "on the heap" */
*ptr = 111; /* OK */
printf("*ptr contains value %d\n", *ptr); /* OK, 111 */
printf("ptr[0] contains value %d\n", ptr[0]); /* OK, 111 */
ptr[0] = 222; /* OK */
printf("*ptr contains value %d\n", *ptr); /* OK, 222 */
printf("ptr[0] contains value %d\n", ptr[0]); /* OK, 222 */
/* Must explicitly free memory after you are done with it, otherwise you have a memory leak */
free(ptr);
return 0;
}
If you make a pointer point to an int, it is safe to read from or write to the de-referenced pointer either using *p or p[0] (which are essentially the same thing), but p[1] is not safe:
#include <stdio.h>
int main(void)
{
int foo = 111;
int bar = 222;
int *ptr = &bar;
printf("Address of ptr %p\n", &ptr); /* An address on the stack */
/* Some random value */
/* Note: this is the address OF the ptr variable, */
/* and not the address stored IN ptr */
printf("Address of bar %p\n", &bar); /* An address on the stack */
/* Exact difference with ptr depends on compiler etc. */
/* On macOS: 12 bytes after address of ptr */
printf("Address of foo %p\n", &foo); /* An address on the stack */
/* Exact difference with bar depends on compiler etc. */
/* On macOS: 4 bytes after address of bar */
printf("foo contains value %d\n", foo); /* 111 */
printf("bar contains value %d\n", bar); /* 222 */
printf("ptr contains address %p\n", ptr); /* The same as address of bar above */
ptr[0] = 333; /* Perfectly safe, changes the value of bar */
printf("bar contains value %d\n", bar); /* 333 */
ptr[1] = 444; /* BAD technically the behavior is undefined */
/* But most likely will change the value of foo */
/* Because most likely foo is stored after bar on the stack */
printf("foo contains value %d\n", foo); /* On macOS: 444 */
return 0;
}
Note [1]: Accidentally "buffer overflows" are a very common problem in C programs, particularly when dealing with strings. This is one of the most common mistakes for novice C programmers. See below for an example. Many viruses exploit such bugs to craft a special string to force a very carefully controlled overflow which causes the function return address (which is also stored on the stack) to be overwritten. When the function returns, instead of returning to the calling function, it returns to some address carefully chosen by the attacker. This allows the attacker to execute some code of her choosing. I think I am not exaggerating when I say that a very large fraction of viruses work in this way. So, be careful and pay very close attention to your memory management!!! See https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/BufferOverflows.html for more details.
#include <stdio.h>
#include <string.h>
int main(void)
{
int foo = 111;
char str[5];
printf("foo contains value %d\n", foo); /* 111 */
strcpy(str, "12345678"); /* BAD: Overflow! Writing 9 bytes into a 5 byte string */
/* (9 not 8, because of the terminating 0 byte ) */
/* Modern operating systems / compilers / processors catch this */
/* On my macOS, I get "Abort trap 6" */
/* Technically, behavior is undefined */
/* In practice, foo is overwritten */
printf("foo contains value %d\n", foo); /* With older operating systems / compilers / */
/* processors this would show a changed value */
return 0;
}
When you declare a local variable it is initialized with a random value that was in the stack. So when you say int *x; the value of that int* is essentially random. The problem of using the pointer in this state is you don't know where it's pointing. Is it valid memory? Is it pointing to memory you don't want to overwrite?
You must initialize a pointer with some value before using it if you expect it to work reliably. This can be an array on the stack or a memory location reserved by malloc.
There are some related discussions about my problem but there are no clearly explanations about my problem.
I thought that I must free() whatever I malloc(), no matter when or where but I have to do it, to prevent a memory leak.
So i have the following program:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int *ptr;
ptr = malloc(256);
if (ptr == NULL) {
printf("Out of memory\n");
return 1;
}
*ptr = 10;
printf("The value of PTR is:\t%d\n",*ptr);
free(ptr);
return 0;
}
I have a pointer and I dynamically allocated some memory (256), then I checked for NULL letter I free() it.
Until here everything is OK: to the pointer was dynamically allocated some memory and then I free() it.
Now i will use a char pointer this time and after i will dynamically allocated some memory (256), i will point that pointer to a string literal, lets say MICHI:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
char *ptr;
ptr = malloc(256);
if (ptr == NULL) {
printf("Out of memory\n");
return 1;
}
ptr = "michi";
printf("%s",ptr);
free(ptr);
return 0;
}
Here I am doing something wrong because if i try to free() it, well it will not work because it happens that i'm going to free a non-heap object.
So i'm lost in here, because i thought that everything you malloc() you have to free() it.
What exactly make that pointer, after points to a string literal, to not need to be free()ed
ptr = malloc(256);
...
ptr = "michi";
printf("%s",ptr);
free(ptr);
As you allocate memory to ptr and then you make it point to string literal. Thus pointer ptr no longer points to memory allocated by malloc.
And to free memory not allocated by malloc or similar functions cause error in program.
Do this instead:
strcpy(ptr,"michi");
In the first program you allocated memory
ptr = malloc(256);
and initialized its first sizeof( int ) bytes with integer value 10.
*ptr = 10;
The pointer itself was not changed. So in this statement
free(ptr);
pointer ptr points to the memory that was allocated using malloc.
In the second program you allocated memory the same way as in the first program
ptr = malloc(256);
But then you reassigned the pointer itself with the address of the first character of string literal "michi"
ptr = "michi";
Compare the left sides of this assignment
*ptr = 10;
and of this assignment
ptr = "michi";
As you see they are different. In the first case you are changing the allocated memory while in the second case you are changing the pointer itself.
Thus in this statement
free(ptr);
pointer ptr does not point to the memory that was previously allocated. It points to the memory occupied by the string literal that was not allocated using malloc.
Thus this statement is wrong and the program has a memory leak because the previous memory that was allocated using malloc was not freed,
Because constant literals are stored in a way for the lifetime of the program, in a read-only region of memory in most of the platforms.
When you use free you are trying to free the memory allocated, not the pointer itself, that's why it's not gonna work if you try to do that in a read-only region of memory
When you reassign your pointer you're losing track of the allocated memory and therefore creating a memory leak.
The memory leak is created because you first allocated the memory and assign it to your pointer, when you've a pointer pointing to it you can easily free that memory, but when you don't have any more reference to that memory you can't.
That's what's happening, when assign the literal to the pointer there's no more reference to that address, it was allocated but it's not reachable anymore, in this way you can't free the memory.
The problem is that you have lost the pointer the second you assigned another value to it:
ptr = malloc(256);
if (ptr == NULL) {
printf("Out of memory\n");
return 1;
}
// here you should free your memory
ptr = "michi";
printf("%s",ptr);
// this fails, because ptr now points to the const static string "michi" you have coded yourself. The assignment didn't copy it, just changed what ptr points to.
free(ptr);
Try this.
#include <stdio.h>
int main(void)
{
char *str:
str = (char*)malloc(sizeof(str) * 4);
printf("Addr of str before: %p\n", str);
str = "Joe";
printf("Addr of str after: %p\n",str);
return(0);
}
/* you will get the following addresses:
Addr of str before: 0x51fc040
Addr of str after: 0x4006f0
this is becuase you changed where str points to. 0x51fc040 is memory allocated using malloc and 0x4006f0 is the memory Adress of the string literal "Joe".
I would advice the following...*/
#include <stdio.h>
int main(void)
{
char *str;
str = (char*)malloc(sizeof(str) * 4);
printf("Addr of str before: %p\n", str);
str =strncpy(str, "Joe", 4);
printf("Addr of str after: %p\n",str);
free(str);
return (0);
}
/* you will get the following addresses:
Addr of str before: 0x51fc040
Addr of str after: 0x51fc040
This is because strncpy copies the string literal to the memory dynamically allocated. and it is safer than stcpy, since it prevents buffer overflow
I feel that working with dynamically-typed languages has fried my intuition about this!
Say if I malloc space for a string and then update that pointer with another malloc (which uses the data from the first), is that a memory leak?
char* my_string = (char*)malloc(length + 1);
(void)snprintf(my_string, length, "blah...");
my_string = some_manipulation(my_string);
Where we have char* some_manipulation(const char* str); assigning memory for its output, which is generated from the provided argument (and may not be the same length).
Is the first malloc now lost, but occupying space, until exit?
Say if I malloc space for a string and then update that pointer with another malloc (which uses the data from the first), is that a memory leak?
Yes, if you do not store the 1st value of string, before overwriting it by the 2nd call to malloc().
Leaking code
char * p = malloc(42);
p = malloc(41);
/* here the program leaks 42 bytes. */
You can only free() the 41 bytes of the 2nd call
free(p);
as the reference to the 42 bytes block is lost.
Non-leaking code
char * p = malloc(42);
char * q = p;
p = malloc(41);
Here you have no leak, as you still can do:
free(p); /* Frees 41 bytes. */
free(q); /* Frees 42 bytes */
which uses the data from the first
All of this does not depend at all on what had been stored (or not) in the memory allocated.
Yes, of course. You seem to think about your string here as an immutable object which is a nice concept, but doesn't leverage you from freeing the memory occupied.
If you know that the call to your function conceptionally invalidates the input string (so, no caller would ever need it again after calling the function), you could instead do something like this:
int some_manipulation(char **str)
{
char *ret = malloc(...);
/* handle error, return -1 */
/* do whatever manipulation */
free(*str);
*str = ret;
return 0;
}
If you have two malloc calls and one free on the same pointer, you almost certainly have a leak (short of the first malloc failing).
Every successful malloc should have an associated free somewhere at the end of the life of the pointer.
Leak:
foo* bar = NULL;
bar = malloc(sizeof(foo) * 10);
if (bar) {
bar = malloc(sizeof(foo) * 20);
}
else {
fprintf(stderr, "Error: Could not allocate memory to bar\n");
exit(EXIT_FAILURE);
}
free(bar);
bar = NULL;
No leak:
foo* bar = NULL;
bar = malloc(sizeof(foo) * 10);
if (bar) {
free(bar);
bar = NULL;
}
else {
fprintf(stderr, "Error: Could not allocate memory to bar\n");
exit(EXIT_FAILURE);
}
bar = malloc(sizeof(foo) * 20);
if (bar) {
free(bar);
bar = NULL;
}
else {
fprintf(stderr, "Error: Could not allocate memory to bar\n");
exit(EXIT_FAILURE);
}