Trying to improve my C skills. This program should take a string and strip it of whitespace.
I'm aware I've made two calls to malloc but have only called free once. Have I thus got some unfreed memory somewhere, or because I assign the original pointer to the new memory, does it all vanish in the one free call I do make?
Thanks!
static void foo() {
char *string_to_change = malloc(sizeof(char) * 256);
strcpy(string_to_change, "my test");
printf("Before: %s\n", string_to_change); // "my test"
strip_whitespace(&string_to_change);
printf("After: %s\n", string_to_change); // "mytest"
free(string_to_change);
}
static void strip_whitespace(char **ptr) {
char *res = malloc(strlen(*ptr));
if (res == NULL)
exit_with_error(NULL_POINTER);
int current_pos = 0;
for (int i = 0; i < strlen(*ptr); i++) {
if (((*ptr)[i] == ' ') || ((*ptr)[i] == '\n' && ((*ptr)[i-1] == '\n' || (*ptr)[i+1] == '\0'))) {
continue;
}
res[current_pos] = (*ptr)[i];
current_pos++;
}
res[current_pos] = '\0';
*ptr = res;
}
You have a leak here:
*ptr = res;
Prior to this line, *ptr points to an allocated block of memory. You then assign to it the starting address of another allocated block, meaning the original pointer is lost. You should free right before this line:
free(*ptr);
*ptr = res;
Also, this allocation is potentially too little:
char *res = malloc(strlen(*ptr));
If *ptr contains no spaces to strip, res won't have enough memory to hold the terminating null byte. You'll need to add 1 to this.
char *res = malloc(strlen(*ptr) + 1);
KISS principle means we should write code as simple as possible, not as complex as possible. That is:
Don't mix up allocation and algorithm in a single unholy mess. Use caller allocation if possible.
Don't use dynamic allocation unless there's an obvious need for it.
It's usually a good idea to regard strings as "immutable" and not make in-place modifications to them unless there's specific requirements for that.
Avoid pointer-to-pointer and weird arithmetic on pointer-to-pointers.
The presence of continue in C programs is almost a dead certain indication of a needlessly complex loop.
Don't re-invent the wheel. There's ctype.h.
If we are to implement this with caller allocation, the code boils down to something compact and easy to read like:
void strip_spaces (char* dst, const char* src)
{
while(*src != '\0')
{
if(!isspace(*src))
{
*dst = *src;
dst++;
}
src++;
}
*dst = '\0';
}
Complete program:
#include <ctype.h>
#include <stdio.h>
void strip_spaces (char* dst, const char* src)
{
while(*src != '\0')
{
if(!isspace(*src)) // if not string
{
*dst = *src; // then copy
dst++;
}
src++;
}
*dst = '\0';
}
int main (void)
{
char str[] = " weird \n\r string contain ing spac\tes\n";
char stripped[128];
strip_spaces(stripped, str);
puts(stripped);
}
Indeed, there is a memory leak inside your code.
Let's say your first call to malloc allocate memory at address 0x0010 and your second call allocate memory at address 0x0100. Your free call will effectively free the memory at address string_to_change which is 0x0100 but nothing tells the compiler to free the memory at 0x0010.
Usually, you really have to have as many free calls as malloc calls.
Related
The problem should be simple, but I have spent hours on this and cannot see what is wrong in my logic. The output works as it should, but Valgrind prints memory issues that should be fixed. I have added the origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char)); code to the while loop, my question is why doesn't this dynamically adjust the memory? The exact error given by Valgrind is
==9== Invalid write of size 1
==9== at 0x1087E2: mystrcat (mystrcat.c:18)
==9== by 0x10883C: main (mystrcat.c:34)
==9== Address 0x522d046 is 6 bytes inside a block of size 7 free'd
==9== at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9== by 0x1087C2: mystrcat (mystrcat.c:17)
==9== by 0x10883C: main (mystrcat.c:34)
==9== Block was alloc'd at
==9== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9== by 0x108811: main (mystrcat.c:31)
char *mystrcat(char *dest, const char *src)
{
char *origdest = dest;
while(*dest) {
dest++;
}
int i = 1;
while (*src) {
origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char));
*dest++ = *src++; // Copies character and increases/moves pointer
i++;
}
*dest = 0;
return origdest;
}
int main(void)
{
char *str = malloc(7);
strcpy(str, "Mydogs");
str = mystrcat(str, "arecool");
printf("%s\n", str);
free(str);
}
This statement:
Address 0x522d046 is 6 bytes inside a block of size 7 free'd is saying that the realloc() called following these statements results in the old pointer pointing to freed memory.
after this segment:
char *origdest = dest;
while(*dest) {
dest++;
}
EDIT to address comment "what is specifically wrong with the code and what could be changed to make it work?"
The explanation of my first observation above is that once the pointer to allocated memory is moved, as you have done, the memory allocation tables no longer have an accurate location of that memory, making that memory un-freeable.
Your stated goal here is to create a version of strcat(), so using realloc() is a reasonable approach, but to use it safely allocate the new memory into a temporary buffer first, then if allocation fails, the original memory location still exists, and can be freed.
One other small change that makes a big difference is how i is initialized. If 1 is used, it places the beginning of the second string one extra position further in memory, leaving a \0 character just after the first string, effectively making it the end of the resultant string. i.e. you would never see the appended part of the string:
In memory it would look like this:
|M|y|d|o|g|s|\0|a|r|e|c|o|o|l|
Then over-flow your buffer when attempting to place another NULL terminator at the and of the concatenated buffer, resulting in undefined behavior.
The following adaptation of your code illustrates these, along with some other simplifications:
char *mystrcat(char *dest, const char *src)
{
char *temp = NULL;
int i = 0;//changed from i = 1 as first location to
//copy to is len1, not len1 + 1
//Note, starting at len1 + 1 would leave a NULL character
//after "Mydogs", effectively ending the string
//the following are simplifications for use in realloc()
int len1 = strlen(dest);
int len2 = strlen(src);
//call once rather than in a loop. It is more efficient.
temp = realloc(dest, len1+len2+1);//do not cast return of realloc
if(!temp)
{
//handle error
return NULL;
}
dest = temp;
while(*src)
{
dest[len1 + i] = *src;
i++;
src++;
}
dest[len1 + i] = 0;//add null terminator
return dest;
}
int main(void)
{
char *temp = NULL;
char *str = malloc(7);
if(str)//always a good idea to test pointer before using
{
strcpy(str, "Mydogs");
temp = mystrcat(str, "arecool");
if(!temp)
{
free(str);
printf("memory allocation error, leaving early");
return 0;
}
str = temp;
printf("%s\n", str);
free(str);
}
return 0;
}
Why it is not correct to cast the return of c-m-realloc() in C.
Here you move to the end of the original string:
while(*dest)
dest++;
Here you allocate some new memory, but dest still points to the end of the original string. So you are overwriting memory after the end of the original string. Since you are reallocating, the original string may not even exist anymore at the previous location you are writing to, because realloc can move the data to a completely new location.
while (*src)
{
origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char));
*dest++ = *src++; // Copies character and increases/moves pointer
i++;
}
How can I implement a function that will concatenate something to a char* (not char array)?
Example of what I want:
#include <stdio.h>
#include <string.h>
int main() {
char* current_line;
char temp[1];
sprintf(temp, "%c", 'A');
// This causes a seg fault. I of course don't want that, so how do I get this to work properly?
strcat(current_line, temp);
return 0;
}
How can I fix this to work properly (and please, tell me if I need to add anything to my question or point me in the right direction because I couldn't find anything)?
Edit: I made this but it seg faults
char* charpointercat(char* mystr, char* toconcat) {
char ret[strlen(mystr) + 1];
for(int i = 0; mystr[i] != '\0'; i++) {
ret[i] = mystr[i];
}
return ret;
}
You have 3 problems:
You do not allocate memory for current_line at all!
You do not allocate enough memory for temp.
You return a pointer to a local variable from charpointercat.
The first one should be obvious, and was explained in comments:
char *current_line only holds a pointer to some bytes, but you need to allocate actual bytes if you want to store something with a function like stracat.
For the secoond one, note that sprintf(temp, "%c", 'A'); needs at least char temp[2] as it will use one byte for the "A", and one byte for terminating null character.
Since sprintf does not know how big temp is, it writes beyond it and that is how you get the segfault.
As for your charpointercat once the function exits, ret no longer exists.
To be more precise:
An array in C is represented by a pointer (a memory address) of its first item (cell).
So, the line return ret; does not return a copy of all the bytes in ret but only a pointer to the first byte.
But that memory address is only valid inside charpointercat function.
Once you try to use it outside, it is "undefined behavior", so anything can happen, including segfault.
There are two ways to fix this:
Learn how to use malloc and allocate memory on the heap.
Pass in a third array to the function so it can store the result there (same way you do with sprintf).
From the first code you posted it seems like you want to concatenate a char to the end of a string... This code will return a new string that consists of the first one followed by the second, it wont change the parameter.
char* charpointercat(char* mystr, char toconcat) {
char *ret = (char*) malloc(sizeof(char)*(strlen(mystr) + 2));
int i;
for(i = 0; mystr[i] != '\0'; i++) {
ret[i] = mystr[i];
}
ret[i] = toconcat;
ret[i + 1] = '\0';
return ret;
}
This should work:
char* charpointercat(char* mystr, char* toconcat) {
size_t l1,l2;
//Get lengths of strings
l1=strlen(mystr);
l2=strlen(toconcat);
//Allocate enough memory for both
char * ret=malloc(l1+l2+1);
strcpy(ret,mystr);
strcat(ret,toconcat);
//Add null terminator
ret[l1+l2]='\0';
return ret;
}
int main(){
char * p=charpointercat("Hello","World");
printf("%s",p);
//Free the memory
free(p);
}
I have a malloced char *str.
char *str = strdup("first\nsecond\nthird\nfourth");
I have a matrix char **content.
char **content;
My objective is to cut str replacing '\n' by '\0' and put each first address after every '\0' in my matrix in order. The following code works:
int count_lines(char *str, char **content)
{
int len;
if (content)
content[0] = str;
len = 0;
while (str && *(str++))
{
if (*str && *(str - 1) == '\n')
{
len++;
if (content)
{
*(str - 1) = 0;
content[len] = str;
}
}
}
if (content)
content[len + 1] = 0;
return (len + 1);
}
void test()
{
char **content;
int lines;
lines = count_lines(str, NULL);
content = malloc(sizeof(char *) * (lines + 1));
count_lines(str, content);
}
Then, content[0] contains "first", contant[1] contains "second" and
[contant[2] contains "third" and contant[3] contains "fourth".
Now my problem is the following one: I got an error when I try to free from the first address. I dont know if what I do brokes the memory or idk.. Help please, how to free this if possible.
When you alloc() memory, the system keeps track of the size of the block you allocated - when freeing, the system frees the same amount was allocated.
So, if you use only one malloc(), you can free() only once - any pointer pointing into the area now freed, gets invalidated.
Hence, you need a free() for "str", and a free() for "content". After freeing str, all the pointers in "content" get invalid, but you must free "content" itself. Or, free "content" first and "str" later.
You have to malloc each of these words. 1 free == 1 malloc. Please paste the code where u try to free
I want to know how to alloacte memory for a char pointer in a function using double pointer and write into the pointer.
I tried to write the following code but it crashes. What is the bug in this?
#include <stdio.h>
void myfunc(const char* src, char** dest)
{
*dest = (char*)malloc(200);
while(*(*dest++) = (*src++ != '\0'));
*(*(++dest)) = '\0';
}
void main()
{
char* src = "hello";
char* dest = null;
myfunc(src, &dest);
printf("%s\n",dest);
}
You've written a compare loop instead of a copy loop ('==' vs '='), and you are incrementing the wrong pointer when you write:
while(*(*dest++) == *src++);
(The additional line:
*(*(++dest)) = '\0';
is a late-breaking addition to the question. I'm not sure I want to try parsing that at all. It is not a part of the solution to the problem. See the discussion below.)
The easiest way to get that correct is probably:
char *tgt = *dest;
while ((*tgt++ = *src++) != '\0')
;
We can correct your code in phases (and I did so like this):
static void myfunc(const char* src, char** dest)
{
*dest = (char *)malloc(200);
char *tgt = *dest;
while ((*(tgt++) = *(src++)) != '\0')
;
}
This parenthesises the expressions in the loop fully. We can now substitute *dest for tgt:
static void myfunc(const char* src, char** dest)
{
*dest = (char *)malloc(200);
char *tgt = *dest;
while ((*((*dest)++) = *(src++)) != '\0')
;
printf("1: %s\n", tgt);
}
And this prints 1: hello, but the main program prints an empty line because you've modified *dest so it points to the NUL '\0' at the end of the copied string. So, you'd need to do:
static void myfunc(const char* src, char** dest)
{
*dest = (char *)malloc(200);
char *tgt = *dest;
while ((*((*dest)++) = *(src++)) != '\0')
;
printf("1: %s\n", tgt);
*dest = tgt;
}
And then main() will print the correct answer. But, if you're doing that dinking with tgt (an abbreviation for 'target'; I usually use dst for destination, but that is too close to your dest), you may as well avoid the complexity of incrementing *dest in the first place.
In fact, you should consider using:
#include <string.h>
...
strcpy(*dest, src);
to copy the string. Using strcpy() is probably better, as in 'faster' and 'simpler' and unequivocally correct.
Also, you should have:
#include <stdlib.h>
to declare malloc().
And the correct return type for main() is int:
int main()
{
...
return(0);
}
In C99, the return is (regrettably) optional and zero (success) will be assumed if it is missing; this matches the behaviour of C++98. In earlier versions of C, the return was not optional.
There are so many problems in that little snippet, I don't know where to start... To sum it up, you have made the code needlessly complex and therefore it ended up full of bugs.
Things to fix to make this code compile:
Unless this is code for an embedded system, or unless you are writing an operative
system, main must return int.
NULL is an upper-case constant in C. It is found in the library stddef.h.
The malloc function is found in stdlib.h, which must be included.
Serious bugs:
Never typecast the result of malloc. More info in the C FAQ and on this SO post.
Always free the memory allocated by malloc.
You assign the boolean result (true/false) of *src++ != '\0' to a character.
Widely-recognized bad & dangerous practice that leads to bugs:
Always declare pointers to string literals as const.
Never use assignment inside conditions. (MISRA-C:2004 13.1).
Never use ++ operators inside complex expressions (MISRA-C:2004 12.13).
Never put a semicolon at the end of a row containing a loop statement. (MISRA-C:2004 14.9)
Never use any statement without braces {} (MISRA-C:2004 14.8).
Poor style:
main() should always return.
Avoid "magic numbers", particularly when passing parameters to malloc.
Always check the result of malloc().
Useful hints:
calloc sets all of the allocated memory to zero, unlike malloc. If you use calloc you don't have to set them to zero manually.
Fixed code:
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#define DYNAMIC_BUF_SIZE 200
void make_string (const char* src, char** dest)
{
*dest = calloc(DYNAMIC_BUF_SIZE, sizeof(char));
if(*dest == NULL)
{
/* error handling here */
}
char* dst = *dest;
*dst = *src;
while(*src != '\0')
{
dst++;
src++;
*dst = *src;
}
}
void delete_string (char* str)
{
free(str);
}
int main()
{
const char* src = "hello";
char* dest = NULL;
make_string (src, &dest);
printf("%s\n",dest);
delete_string(dest);
return 0;
}
EDIT: New version without strcpy(), as requested by OP.
//It seems that you don't understand the nature of char* and char**.
char *str = "hello! I am from China and i want to make friends with foreigners";
char **ptr = {"hello!","i want to learn spoken English","sinalym#163.com"};
//Allocate memory for a char** variable. Two steps as follows:
int length[3] = {6,31,16};
char **ptr2 = new char*[3];
for(int i = 0;i < length[i];i++)
*(ptr2 + i) = new char [length[i]];
//delete according to a reverse order.
Bear with me. I have not coded in c in 8 years and am totally baffled why my string manipulation is not working. I am writing a program that loops forever. In the loop I initialize two char pointers each is passed to a function that add text to the char pointer (array). When the functions are done I print the char pointer and free the two char pointers. However the program dies after 7 iterations with the following error message
* glibc detected * ./test: double free or corruption (fasttop): 0x0804a168 ***
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include string.h
#include stdio.h
#include stdlib.h
#include errno.h
#include time.h
char *SEPERATOR = "|";
void getEvent (char* results);
void getTimeStamp(char* timeStamp, int timeStampSize);
void stringAppend(char* str1, char* str2);
int main (int argc, char *argv[])
{
int i = 0;
while(1)
{
i++;
printf("%i", i);
char* events= realloc(NULL, 1);
events[0] = '\0';
getEvent(events);
char* timestamp= realloc(NULL, 20);
timestamp[0] = '\0';
getTimeStamp(timestamp, 20);
printf("%s", events);
printf("timestamp: %s\n", timestamp);
free(events);
free(timestamp);
}
}
void getEvent (char* results)
{
stringAppend(results, "a111111111111");
stringAppend(results, "b2222222222222");
}
void getTimeStamp(char* timeStamp, int timeStampSize)
{
struct tm *ptr;
time_t lt;
lt = time(NULL);
ptr = localtime(<);
int r = strftime(timeStamp, timeStampSize, "%Y-%m-%d %H:%M:%S", ptr);
}
void stringAppend(char* str1, char* str2)
{
int arrayLength = strlen(str1) + strlen(str2) + strlen(SEPERATOR) + 1;
printf("--%i--",arrayLength);
str1 = realloc(str1, arrayLength);
if (str1 != NULL)
{
strcat(str1, SEPERATOR);
strcat(str1, str2);
}
else
{
printf("UNABLE TO ALLOCATE MEMORY\n");
}
}
You are reallocating str1 but not passing the value out of your function, so the potentially changed pointer is leaked, and the old value, which has been freed by realloc, is freed again by you. This causes the "double free" warning.
The problem is that while stringAppend reallocates the pointers, only stringAppend is aware of this fact. You need to modify stringAppend to take pointer-to-pointers (char **) so that the original pointers are updated.
This line in stringAppend:
str1 = realloc(str1, arrayLength);
changes the value of a local variable in stringAppend. This local variable named str1 now points to either the reallocated memory or NULL.
Meanwhile local variables in getEvent keep the values they had before, which now usually point to freed memory.
All the comments where very helpfull. Of course it makes total sense why the error was happening. I ended up solving it by making the following changes.
For both the getEvent and stringAppend I return the char pointer.
e.g.
char* stringAppend(char* str1, char* str2)
{
int arrayLength = strlen(str1) + strlen(str2) + strlen(SEPERATOR) + 1;
printf("--%i--",arrayLength);
str1 = realloc(str1, arrayLength);
if (str1 != NULL)
{
strcat(str1, SEPERATOR);
strcat(str1, str2);
}
else
{
printf("UNABLE TO ALLOCATE MEMORY\n");
}
return str1;
}
This isn't an answer to your question (and you don't need one, since the error has been pointed out), but I do have some other comments about your code:
char* events= realloc(NULL, 1);
events[0] = '\0';
You don't test that realloc successfully allocated memory.
char* timestamp= realloc(NULL, 20);
timestamp[0] = '\0';
Same problem here. In this case, you don't need realloc at all. Since this is a fixed-size buffer, you could use just:
char timestamp[20] = "";
And don't do this:
str1 = realloc(str1, arrayLength);
because if realloc fails, you'll orphan the memory that str1 was pointing to before. Instead:
char* temp = realloc(str1, arrayLength);
if (temp != NULL)
{
str1 = temp;
...
}
Note that since you're modifying stringAppend to return the new string, you should do similar checks in the calling functions.
Also, "separator" is spelled with two As, not with two Es.