I know this has been asked quite a bit, but every example I looked at never seemed to fit exactly. In the code below if I keep the free(), the resulting compiled binary segfaults. If I remove it, the code works just fine. My question is, why?
int convertTimeToStr(time_t* seconds, char* str)
{
int rc = 0;
if (str == NULL) {
printf("The passed in char array was null!\n");
rc = 1;
} else {
char* buf = malloc(sizeof(char) * 100);
memset(buf, '\0', sizeof(buf));
buf = asctime(gmtime(seconds));
strcpy(str, buf);
free(buf);
}
return rc;
}
The problem is that you reassign the pointer to your allocated memory. What you're doing is basically equivalent to
int a = 5;
int b = 10;
a = b;
and then wondering why a is no longer equal to 5.
With the assignment buf = asctime(gmtime(seconds)) you lose the original pointer and have a memory leak.
What the asctime function returns is a pointer to a static internal buffer, it's not something you should pass to free.
You should not be surprised by that, since you've changed the value of the pointer buf from what malloc() returned.
char* buf = malloc(sizeof(char) * 100); // value returned by malloc()
memset(buf, '\0', sizeof(buf));
buf = asctime(gmtime(seconds)); // change value of buf
strcpy(str, buf);
free(buf); // buf differs from above
Calling free() with an argument that was not returned from malloc() (or calling it for the second time) is undefined behaviour.
You call malloc and memset, which allocates a buffer and sets it to zeroes, but then you overwrite the value of buf with the return value from asctime. By the time you call free, it is on the return value from asctime, not your original allocation. This has three issues:
You never use the buffer you allocated with malloc for any useful purpose, so you don't need that malloc nor the memset
You lose the pointer returned by malloc so you can never free it. Your process has leaked memory.
You try to free the return value from asctime. The return value from asctime does not need to be freed and should not be freed. This causes undefined behavior, in your case a segfault.
Related
Sample program:
#include <stdio.h>
#include <malloc.h>
void f(int n) {
char *val = (char *) malloc(12*sizeof(char));
val = "feels....";
printf("%s", val);
// free val; // if enable, compile time error: expected ';' before 'val' free val;
}
int main()
{
f(1);
return 0;
}
Is it required to free the memory which is dynamically allocated ? if yes, how to.
Yes, you need to free the memory. But when you allocate memory for a string, the way to populate the string is not to assign a string to it as that replaces the memory you've allocated. Instead you're meant to use the function strcpy like this...
char *val = malloc(12*sizeof(char));
strcpy(val,"feels....");
printf("%s", val);
free(val);
Instead of this:
char *val = (char *) malloc(12*sizeof(char));
val = "feels...."; // val points now to the string literal ""feels...."
// discarding the value returned by malloc
...
free(val); // attempt to free the string literal which will
// result in undefined behaviour (most likely a crash)
you probably want this:
char *val = malloc(12*sizeof(char)); // in C you don't cast the return value of malloc
strcpy(val, "feels...."); // the string "feels...." will be copied into
// the allocated buffer
...
free(val); // free memory returned previously by malloc
The compilation problem is because free is a function, you need to put its argument in parentheses.
free(val);
The other problem is a memory leak.
Strings in C are really just pointers to (hopefully) blocks of memory containing char data. The end of the string is denoted by a char with value 0. The thing to remember is that your variable is simply a pointer like any other pointer. So...
char *val = (char *) malloc(12*sizeof(char));
The above line dynamically allocates a block of memory and assigns a pointer to it to val.
val = "feels....";
The above line assigns a pointer to a string literal to val overwriting the previous pointer that was in val. It has not touched, in any way, the block of memory that was malloced in the first line. Furthermore, you have lost any reference you had to the malloced block so it has leaked. There's no way to free it.
String literals are usually created at compile time and the memory they occupy will be part of the program. This means they haven't come from the heap (where malloc gets its memory from. This means, in turn, when you try to free a string literal, bad things happen. On modern architectures, the program text is protected from writes at the OS level so trying to free part of it will almost certainly crash your program.
As long as you do not want to change the content of the string, you do not need to malloc space to it. You can omit the malloc line (and the corresponding free) and your program will still work.
f you do want to change the string, the easiest way to get a mutable copy of a string literal is to use strdup:
char *val = strdup("feels....");
// Do stuff with the string
free(val); // strdup strings need to be freed
strdup is a Posix function but not a C standard function so your platform might not have it. It's pretty simple to implement your own, though.
char* myStrDup(const char* thingToDup)
{
char* ret = malloc(strlen(thingToDup) + 1); // strlen returns the length without the terminating nul. Hence add 1 to it to allocate
strcpy(ret, thingToDup); // Copies the entire string including the terminating nul.
return ret;
}
I'm trying to wrap my head around the concept of heap-management in C. The following code compiles w/o warnings and runs w/o errors.
char* string_make(char* text) {
size_t len = strlen(text) + 1;
char* str = malloc(len);
memcpy(str, text, len);
return str;
}
char* string_concat(char* x, char* y) {
size_t len_x = strlen(x);
size_t len_y = strlen(y);
x = realloc(x, len_x + len_y + 1);
memcpy(x+len_x, y, len_y+1);
return x;
}
int main (int argc, char const *argv[]) {
char* first = string_make("funny");
char* second = string_make(" duck");
char* third = string_make("! c++");
//
printf("%s\n", string_concat(first, second));
printf("%s\n", string_concat(first, third));
//
free(first);
free(second);
free(third);
}
I have a couple of questions:
Is string_make() doing anything illegal or undefined?
Is string_concat() doing anything illegal or undefined?
I just want to create a simple heap string, which could be increased/decreased at different stages of program as per requirements.
Thanks.
EDIT:
If I change string_concat() calls in main to the following:
first = string_concat(first, second);
first = string_concat(first, third);
would it make things legit?
string_make is fine.
string_concat is not. It reallocates x, which possibly means that a bigger chunk of memory is allocated elsewhere, and the original chunk of memory is marked as free. However, functions in C do not change their arguments, therefore when string_concat returns, x possibly points to a location which is marked as free.
Ignoring error checking (after malloc and realloc), all you need to do is replace these two lines in main:
printf("%s\n", string_concat(first, second));
printf("%s\n", string_concat(first, third));
with these lines
first = string_concat(first, second);
printf("%s\n", first);
first = string_concat(first, third);
printf("%s\n", first);
The reason is that arguments are passed by value in C. So updating x in the string_concat does not update variable first in main. So the code needs to update first using the return value from the function.
Now, you may be confused because you tested your code and it seemed to work. That's because your final string was only 16 bytes, including the NUL terminator. Most modern implementations of malloc will round up the size to a multiple of 16 (or some larger power of 2). That means that all three calls to string_make returned pointers to memory regions of 16 bytes, even though you requested less. And that also means that realloc can expand the buffer without moving it. As the man page explains
If there is not enough room to enlarge the memory allocation
pointed to by ptr, realloc() creates a new allocation, copies as much
of the old data pointed to by ptr as will fit to the new allocation,
frees the old allocation, and returns a pointer to the allocated
memory.
In your case, there was enough room to enlarge the memory allocation, so realloc returned the same pointer that was passed in. And as a result, your code would appear to work even though it has a serious flaw.
string_concat(): If using realloc() I would pass the address of the pointer to the string which has to be realloc-ed, but return the reallocated string itself. So I would avoid the return value pointing at location possibly marked as free. In case realloc returns NULL, function returns pointer to the original string.
char* string_concat(char** x, char* y) {
size_t len_x = strlen(*x);
size_t len_y = strlen(y);
char *temp = realloc(*x, len_x + len_y + 1);
if(!temp) return *x; //or do some more appropriate error handling
memcpy(temp+len_x, y, len_y+1);
*x = temp;
return *x;
}
and
printf("%s\n", string_concat(&first, second));
printf("%s\n", string_concat(&first, third));
free(first);
free(second);
free(third);
It is well explained here.
Why the size of the following allocated string (i.e. result) is not correct in the following code
#include <stdlib.h>
char *concat(const char *s1, const char *s2)
{
char *result;
result = malloc( strlen(s1) + strlen(s2) + 1 );
printf("%d", sizeof(result)); // <-- ????? ( it should be seven)
if ( result == NULL ){
printf("Error: malloc failed in concat\n");
exit(EXIT_FAILURE);
}
strcpy(result, s1);
strcpy(result, s2);
return result;
}
int main()
{
char *p;
p = concat("abc", "def");
return 0;
}
Edit:
I'm trying to allocate an enough size for the new string but for somehow the size is not correct.
In this case sizeof is evaluated at compile time, and it pays no attention to the size of the allocated block.
The expression returns the size of pointer, which is fixed for the hardware platform.
Unfortunately, there is no way of finding the allocated size returned by malloc. If you want to know the size of an allocated block, you need to store the size in a separate variable.
sizeof(result) will give the size of a pointer probably 4 or 8, so you cant use it for that.
Immediately after calling malloc you should check it's return value, on error it returns NULL, if the value is not NULL then it points to uninitialized content.
To get the length of a string you need strlen function, but if the contents are not initialised it would be undefined behavior.
You need to use strlen after you fill the buffer with data, the data is terminated with a '\0' byte which marks the end of the string, it wouldn't be there if you haven't initialized the data.
I am writing a program where the input will be taken from stdin. The first input will be an integer which says the number of strings to be read from stdin.
I just read the string character-by-character into a dynamically allocated memory and displays it once the string ends.
But when the string is larger than allocated size, I am reallocating the memory using realloc. But even if I use memcpy, the program works. Is it undefined behavior to not use memcpy? But the example Using Realloc in C does not use memcpy. So which one is the correct way to do it? And is my program shown below correct?
/* ss.c
* Gets number of input strings to be read from the stdin and displays them.
* Realloc dynamically allocated memory to get strings from stdin depending on
* the string length.
*/
#include <stdio.h>
#include <stdlib.h>
int display_mem_alloc_error();
enum {
CHUNK_SIZE = 31,
};
int display_mem_alloc_error() {
fprintf(stderr, "\nError allocating memory");
exit(1);
}
int main(int argc, char **argv) {
int numStr; //number of input strings
int curSize = CHUNK_SIZE; //currently allocated chunk size
int i = 0; //counter
int len = 0; //length of the current string
int c; //will contain a character
char *str = NULL; //will contain the input string
char *str_cp = NULL; //will point to str
char *str_tmp = NULL; //used for realloc
str = malloc(sizeof(*str) * CHUNK_SIZE);
if (str == NULL) {
display_mem_alloc_error();
}
str_cp = str; //store the reference to the allocated memory
scanf("%d\n", &numStr); //get the number of input strings
while (i != numStr) {
if (i >= 1) { //reset
str = str_cp;
len = 0;
}
c = getchar();
while (c != '\n' && c != '\r') {
*str = (char *) c;
printf("\nlen: %d -> *str: %c", len, *str);
str = str + 1;
len = len + 1;
*str = '\0';
c = getchar();
if (curSize/len == 1) {
curSize = curSize + CHUNK_SIZE;
str_tmp = realloc(str_cp, sizeof(*str_cp) * curSize);
if (str_tmp == NULL) {
display_mem_alloc_error();
}
memcpy(str_tmp, str_cp, curSize); // NB: seems to work without memcpy
printf("\nstr_tmp: %d", str_tmp);
printf("\nstr: %d", str);
printf("\nstr_cp: %d\n", str_cp);
}
}
i = i + 1;
printf("\nEntered string: %s\n", str_cp);
}
return 0;
}
/* -----------------
//input-output
gcc -o ss ss.c
./ss < in.txt
// in.txt
1
abcdefghijklmnopqrstuvwxyzabcdefghij
// output
// [..snip..]
Entered string:
abcdefghijklmnopqrstuvwxyzabcdefghij
-------------------- */
Thanks.
Your program is not quite correct. You need to remove the call to memcpy to avoid an occasional, hard to diagnose bug.
From the realloc man page
The realloc() function changes the size of the memory block pointed to
by ptr to size bytes. The contents will be unchanged in the range
from the start of the region up to the minimum of the old and new
sizes
So, you don't need to call memcpy after realloc. In fact, doing so is wrong because your previous heap cell may have been freed inside the realloc call. If it was freed, it now points to memory with unpredictable content.
C11 standard (PDF), section 7.22.3.4 paragraph 2:
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 contents of the new object shall be the same as that of the old object prior to deallocation, up to the lesser of the new and old sizes. Any bytes in the new object beyond the size of the old object have indeterminate values.
So in short, the memcpy is unnecessary and indeed wrong. Wrong for two reasons:
If realloc has freed your previous memory, then you are accessing memory that is not yours.
If realloc has just enlarged your previous memory, you are giving memcpy two pointers that point to the same area. memcpy has a restrict qualifier on both its input pointers which means it is undefined behavior if they point to the same object. (Side note: memmove doesn't have this restriction)
Realloc enlarge the memory size where reserved for your string. If it is possible to enlarge it without moving the datas, those will stay in place. If it cannot, it malloc a lager memory plage, and memcpy itself the data contained in the previous memory plage.
In short, it is normal that you dont have to call memcpy after realloc.
From the man page:
The realloc() function tries to change the size of the allocation pointed
to by ptr to size, and returns ptr. If there is not enough room to
enlarge the memory allocation pointed to by ptr, realloc() creates a new
allocation, copies as much of the old data pointed to by ptr as will fit
to the new allocation, frees the old allocation, and returns a pointer to
the allocated memory. If ptr is NULL, realloc() is identical to a call
to malloc() for size bytes. If size is zero and ptr is not NULL, a new,
minimum sized object is allocated and the original object is freed. When
extending a region allocated with calloc(3), realloc(3) does not guaran-
tee that the additional memory is also zero-filled.
I'm trying reallocate more 256 bytes to buffer on each loop call. In this buffer, I will store the buffer obtained from read().
Here is my code:
#define MAX_BUFFER_SIZE 256
//....
int sockfd = socket( ... );
char *buffer;
buffer = malloc( MAX_BUFFER_SIZE );
assert(NULL != buffer);
char *tbuf = malloc(MAX_BUFFER_SIZE);
char *p = buffer;
int size = MAX_BUFFER_SIZE;
while( read(sockfd, tbuf, MAX_BUFFER_SIZE) > 0 ) {
while(*tbuf) *p++ = *tbuf++;
size = size + MAX_BUFFER_SIZE; // it is the right size for it?
buffer = realloc(buffer, size);
assert(NULL != buffer);
}
printf("%s", buffer);
free(tbuf);
free(p);
free(buffer);
close(sockfd);
But the above code returns segment fault. Where am I wrong?
These are the problems that are apparent to me:
Your realloc can modify the location to which buffer points. But you fail to modify p accordingly and it is left pointing into the previous buffer. That's clearly an error.
I see potential for another error in that the while loop need not terminate and could run off the end of the buffer. This is the most likely cause of your segmentation fault.
The way you use realloc is wrong. If the call to realloc fails then you can no longer free the original buffer. You should assign the return value of realloc to a temporary variable and check for errors before overwriting the buffer variable.
You should not call free on the pointer p. Since that is meant to point into the block owned by buffer, you call free on buffer alone.
Thing is read doesn't add a 0-terminator. So your inner while is undoubtedly stepping outside the allocated memory:
while(*tbuf) *p++ = *tbuf++;
Another problem is that you are freeing stuff you didn't receive via malloc. By the time you call free, you will have incremented both p and tbuff which you try to free.
The whole buffer allocation things looks useless as you're not actually using it anywhere.
When you use realloc on buffer, it is possible that the address of buffer is changed as a result of changing the size. Once that happens, p is no longer holding the correct address.
Also towards the end, you are freeing both p and buffer while they point to the same location. You should only free one of them.