How to free a dynamically allocated string divided into a matrix? - c

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

Related

Have I created a memory leak in this function?

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.

C programming problem in dynamic memory allocation

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 to concatenate to a char* and not char array?

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);
}

How to improve the code for dynamically allocated strings in C?

So I'm practicing and learning C right now and came across a rather easy challenge from CodeWars that asked to print out a string of "Aa~", "Pa!", and "Aa!" depending on whether n was <= 6 or not. I know how we can do this with arrays but wanted to try out using dynamically allocated char arrays like with malloc for efficiency sake.
I want to make sure I get the basics down with these questions
So I know in other malloc examples with int we set up a pointer (of type int) to point to an allocated memory block. Is it still a pointer when I declare "char *ptr" to point to an allocated memory block because pardon me I thought "char *anything" meant it was a convention to represent a string. So not sure why the below is working somewhat if I'm not setting up a pointer like "char **ptr" as I thought.
why do I get a "malloc: *** error for object 0x100000fab: pointer being freed was not allocated" type of error when I try to return the answer especially when the "val" is 1 or 0? I read somewhere that changing the pointer(string?), answer, to NULL will solve the issue but not entirely sure why this works.
3 To continue in general with the above question, for freeing up space, what is the best method to do so if we dynamically allocate a memory block in a function but need to return a value from that function? As in, do we free up the space after or before?
Thank you all for your input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define val 1
char *sc(int); // function declaration/prototype
int main(int argc, const char * argv[]) {
char *answer = sc(val);
printf("The answer is %s\n", answer);
answer = NULL; // why does this work
free(answer);
return 0;
}
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
char *ptr = (char*) malloc(n*4);
if (ptr == NULL){
printf("malloc failed");
}
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
ptr = "";
}
else {
printf("Error!");
exit(0);
}
return ptr;
}
The main issue you have with your code is that you're not zeroing out the buffer you get from malloc(), so the first strcat() is not necessarily going to write at the start of the string but at the end.
You can fix that with a strcpy() right after the malloc() call and check:
strcpy(ptr, "");
Or, equivalently, you can just set the first byte of the buffer to zero. Since C strings are zero-terminated strings, setting a character to zero will indicate it's at the end:
ptr[0] = 0;
You also seem to be allocating your buffer too short. If you write n-1 copies of Aa~ (4 bytes) plus one copy of Pa! Aa! (8 bytes when you include the terminating zero!) you'll actually need 4 * (n+1) as space. So either always allocate that or do so in case n < 6 which is where you need the additional bytes.
This is also a problem:
ptr = "";
Because now your ptr is no longer pointing to a buffer returned by malloc(), but to a static (empty) string in your binary. It's quite probable that this is the place where you're getting the trouble from free(), since calling it on a static string in your binary is definitely wrong.
Furthermore, after you set ptr = "" you no longer have any reference to the buffer you allocated, which means you most likely just created a memory leak!
In that case, you should simply use strcpy() or set the first byte to zero. But if you do that at the start of the program, you don't need to do it here.
Finally, free(NULL); works (as in, doesn't throw an error) because that's part of its specification, you can pass it a NULL pointer and it will do nothing. But note that it's not freeing the buffer you allocated, so you have a memory leak here too.
I'd further refactor the second part of your code so you don't have too much repetition appending the strings:
char *sc(int n) {
/* if n <= 6 then will have an extra "Aa!"
* after "Pa!" at the nth position.
*/
char *ptr;
if (n < 0) {
printf("Error!");
return NULL;
}
ptr = (char*) malloc(4 * (n+1));
if (ptr == NULL){
printf("malloc failed");
return NULL;
}
strcpy(ptr, "");
if (n > 1) {
for (int i = 1; i < n; i++){
strcat(ptr, "Aa~ ");
}
strcat(ptr, "Pa!");
if (n <= 6) {
strcat(ptr, " Ah!");
}
}
return ptr;
}
Also note you don't need to assign the result of strcat() back to ptr every time, since it always returns its first argument anyways, so assigning it there isn't changing anything really.
I thought "char *anything" meant it was a convention to represent a string. So not sure why the below is working somewhat if I'm not setting up a pointer like "char **ptr" as I thought.
You're allocating memory for a string, so it just needs to be char *. char ** would be used for an array of multiple strings, or for a pointer to a variable that contains a pointer to a string.
why do I get a "malloc: *** error for object 0x100000fab: pointer being freed was not allocated"
You get that when you do ptr = "";. After you do this, ptr no longer points to the memory that was allocated with malloc, it points to that string literal. If you want to set the allocated memory to an empty string, you can do
ptr[0] = '\0';
This puts a null terminator in the first element of the string.
You also need to do that before the code that uses strcat() to append to the string. Otherwise you're appending to uninitialized data. Simplest would be to do it immediately after allocating the memory (then you don't need it in the n <= 1 block.
The else block is not needed. There are no other possibilities than the 3 you test for unless the CPU is malfunctioning (in which case all bets are off). However, you should check for n < 1 before calling malloc(), as you can't allocate negative memory, and malloc(0) may return NULL.
When you allocate space for ptr, you need to add 1 byte for the terminating null of the string.
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
if (n >= 1) {
char *ptr = malloc(n*4 + 1);
} else {
char *ptr = malloc(1);
}
if (ptr == NULL){
printf("malloc failed");
exit(1);
}
ptr[0] = '\0'; // initialize empty string
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
// nothing to do
}
return ptr;
}

When I malloc more space for a string within an array of strings, the array of strings duplicates some strings

I have an array of strings, and I am trying to malloc more space for one of those strings so I can change the value of the string.
int catenate_strings (char** arr, int index1, int index2) {
char *new_string;
new_string = malloc(1000*sizeof(char));
if (new_string == NULL) {
printf("\nError allocating memory\n");
}
strcpy(new_string, arr[index1]);
strcat(new_string, arr[index2]);
arr[index1] = new_string;
}
However when I run my code, it will work for some instances, but in others it will duplicate the string at index1 and put it at index1 + 1 as well.
Your code has some problems:
Memory leak in arr[index1] = new_string because you are not freeing the old buffer.
Buffer overflow if the result string is longer than 1000 bytes.
Not returning any value from catenate_strings despite the function having return value int.
If all entries in arr are allocated using malloc then you can use realloc.
int catenate_strings (char** arr, int index1, int index2)
{
// Resize buffer to hold old string + new string (+ terminating null byte!)
char * const new_string = realloc(strlen(arr[index1]) + strlen(arr[index2]) + 1);
if (new_string == NULL) {
printf("\nError allocating Memory, cannot resize string\n");
return -1;
}
strcat(new_string, arr[index2]);
arr[index1] = new_string;
return 0;
}
The duplication to index+1 comes not from the shown code but from somewhere else in your code.

Resources