strtok() with realloc() weird behaviour - c

I have the following program written in C:
...
char *answer = NULL;
char *pch = strtok(phrase, " "); // phrase is a string with possibly many words
while (pch) {
char *tmp = translate_word(pch); // returns a string based on pch
void *ptr = realloc(answer, sizeof(answer) + sizeof(tmp) + 1000); // allocate space to answer
if (!ptr) // If realloc fails
return -1;
strcat(answer, tmp); // append tmp to answer
pch = strtok(NULL, " "); // find next word
}
...
The problem is that strtok() shows weird behavior, it returns a word that does not exist in the phrase string but is part of the answer string.
On the other hand, when I change the following line:
void *ptr = realloc(answer, sizeof(answer) + sizeof(tmp) + 1000);
to:
void *ptr = realloc(answer, sizeof(answer) + sizeof(tmp) + 1);
strok() works as expected.
How is it possible that realloc() affects strtok() in this case? They do not even use the same variables. Looking forward to your insights.

The realloc function could move the memory that was previously allocated. After the call, the pointer to the allocated memory is returned and the pointer value passed to it, if it differs, is no longer valid. So when you call strcat(answer, tmp); you're potentially writing to freed memory which invokes undefined behavior, and in this case it manifests as the strange output you're seeing.
After checking the return value of realloc, assign that value back to answer.
Also, sizeof(answer) and sizeof(tmp) give you the size of the pointer, not the size of what it points to. You instead want to use strlen to get the length of the string then contain. And while we're at it, lets just add 1 to this instead of 1000 because that's all you actually need.
void *ptr = realloc(answer, strlen(answer) + strlen(tmp) + 1);
if (!ptr)
return -1;
answer = ptr;
strcat(answer, tmp);
One more issue: the first time realloc is called the memory is completely uninitialized. Subsequently calling strcat on it depends on answer containing a null terminated string. It doesn't so this also invokes undefined behavior.
This can be fixed by malloc-ing a single byte to start and setting it to 0, that way you start with an empty string.
char *answer = malloc(1);
if (!answer) return -1;
answer[0] = 0;

sizeof(answer) & sizeof(tmp) gives you sizes of the pointers.
You need to use strlen instead

additionally...
char *answer = NULL;
... either:
... strlen(answer) ...
strcat(answer, tmp);
These SHOULD fail, with a segmentation violation, but may not depending on the OS. Dereferencing NULL is never a good idea.
In short, you need to either know you have assigned something to answer, or to check if answer is NULL.

Related

Error :"pointer being realloc'd was not allocated" on macOS but works on Windows when trying to realloc twice

I'm trying to implement a function that concatenate two strings, but I keep getting the same error.
"pointer being realloc'd was not allocated"
When I compiled the same code on a windows machine it worked, is it something that I'm missing?
The code below is basically what I'm trying to do.
main:
int main() {
int length = 4096;
char *string = malloc(length * sizeof(char));
createString(string, length);
realloc(string, 30);
return 0;
}
createString:
void createString(char * string, int length) {
char *copyAdress = string;
char *temp ="";
int counter2 = 0;
fflush(stdin);
fgets(string, length,stdin);
while(*string != EOF && *string != *temp ) {
string++;
counter++;
}
string = copyAdress;
realloc(string, (counter)*sizeof(char));
}
Thanks!
Edit:
I want createString to change the size of string to the length of the string that I get with fgets, while having the same address as the string that I sent in, so I can allocate more memory to it later when I want to add another string to it.
There are several issues:
realloc(string, (counter)*sizeof(char)); is wrong, you need string = realloc(string, (counter)*sizeof(char)); because realloc may return a different address.
Calling createString(string, length); won't modify string
If you want a more accurate answer you need to tell us what exactly createString is supposed to do. In your code there is no attempt to concatenate two strings.
Let's work through this in order of execution.
fflush(stdin); is undefined behaviour. If you really need to clear everything in the stdin you have to find another way (a loop for example). There are compilers/systems with a defined implementation but I would not count on it.
string++; is superflous as you overwrite string after the loop.
realloc(string, (counter)*sizeof(char));
should be
char *temp = realloc(string, (counter)*sizeof(char));
if (temp != NULL)
string = temp;
This way you get the pointer where your new string is located, but I suggest you read the refecerence for realloc. In essence you do not know if it has been moved and the old address might be invalid from that point on. So dereferencing it is also undefined behaviour.
After this you would have to return the new address of string or pass the address of the pointer to your function.
The same problem repeats with the second realloc. You only got to know your first call was wrong, because the second call noticed that you do not have valid data in what you thought would be your string.
In regards to your comment: It is not possible to use realloc and to be sure that the reallocated memory is in the same place as before.
If you realloc some memory, the pointer pointing to the original memory becomes invalid (unless realloc failed and returned NULL). So calling realloc twice on the same pointer should indeed not work (if it didn't return NULL the first time).
See the answers from others about what you do wrong. However, the eror message means that on MacOS, the realloc in createString deallocated the orignal string and allocated a new one, and now your realloc in main tries to realloc a pointer that is no longer valid (allocated). On Windows, the memory was not deallocated in createString and so the second call of realloc (in main) is given a valid pointer.

Simple c function, segfaults on free

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.

Raw Heap String

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.

C - segmentation core dump on strcpy()

I get a segmentation fault each time I'm trying to run this function.
char *hist_array[20];
int history_counter = 0;
void save_to_history(char *temp){
temp = malloc(512);/*512 is the size of temp array*/
printf("temp = %s\narray = %s",temp,hist_array[history_counter]);/*debug*/
strcpy(hist_array[history_counter],temp);
printf("Saved %s to history to %d\n\n",hist_array[history_counter],history_counter);
history_counter++;
}
I'm not sure whether I'm using malloc correctly or not, but as I understand it should help with properly saving my string temp to an array of strings hist_array. Also, temp is never NULL.
EDIT 1: Changed sizeof(temp) to its proper size 512, still getting segfault.
The problem is with the following statement -
strcpy(hist_array[history_counter], temp);
strcpy tries to copy the buffer pointed to by temp to the buffer pointed to by hist_array[history_counter] but char *hist_array[20]; defines hist_array to be an array of 20 pointers to characters. You should change your function to -
char *hist_array[20];
int history_counter = 0;
void save_to_history(char *temp) {
// +1 for the terminating null byte as strlen
// does not count the null byte
hist_array[history_counter] = malloc(1 + strlen(temp));
if(hist_array[history_counter] == NULL) {
printf("not enough memory to allocate\n");
// handle it
}
strcpy(hist_array[history_counter], temp);
printf("Saved %s to history to %d\n\n",
hist_array[history_counter],
history_counter);
history_counter++;
}
In your code, you are allocating via malloc but you don't initialize it. So when you do this:
printf("temp = %s\narray = %s",temp,hist_array[history_counter]);/*debug*/
strcpy(hist_array[history_counter],temp);
The should print some garbage and the subsequent strcpy will copy whatever is in temp at the time, until it encounters a 0 byte. You are invoking undefined behaviour here, because you should initialize the memory.
For example you could do:
memset(temp, 0, 512);
Additionally you are dereferencing pointers in your array hist_array. Since this is a global variable, all the pointers will originally be NULL pointers, which is causing your segfaul there.

Passing strings by value in C

I have two functions, one that creates a pointer to a string and another that manipulates it. I somehow am missing something critical, however:
int foo() {
char * mystring; // Create pointer
bar(&mystring); // Pass in address
printf("%s\n", mystring);
return 0; // There's a reason for this
}
int bar(char ** mystring) {
mystring[0] = malloc(strlen(mystring) + 1); // Since malloc will persist even after exiting bar
*mystring = "hahaha"; // Dereference
return 0;
}
Any enlightenment for my addled brain would be greatly appreciated!
C doesn't have strings as first class values; you need to use strcpy() to assign strings.
strcpy(mystring[0], "hahaha");
In addition to the other answers given, note that:
mystring[0]
is the same as
*(mystring + 0)
which is the same as
*mystring
Your malloc allocates the memory and the pointer is written to mystring but it is overwritten by the next line.
The use of malloc is necessary, but this way:
mystring[0] = malloc(strlen(mystring) + 1);
is wrong, since you can't perform strlen on mystring(because it doesn't contain any string yet and because the pointer itself is not initialized). Allocate buffer with the size of your string. for example:
int bar(char ** mystring) {
char* hello = "hahaha";
*mystring = malloc(strlen(hello) + 1);
strcpy(*mystring, hello);
return 0;
}
BTW, you could use the assignment *mystring = "hahaha"; without the malloc since this is a string stored in the data section, and the data will not be lost after returning from the function, but this way it is read-only data and you cannot modify it. The strcpy is there to copy the string to the allocated buffer.

Resources