#include <stdio.h>
#include <stdlib.h>
int ft_strlen(char *str)
{
int i = 0;
if (str)
while (str[i])
i++;
return (i);
}
void ft_append(char **str, char c)
{
int len = ft_strlen(*str);
char *str2 = *str;
char *newstr = malloc(len + 2);
int i = 0;
if (len != 0)
{
while (i < len)
{
newstr[i] = str2[i];
i++;
}
}
newstr[i] = c;
newstr[i + 1] = '\0';
*str = newstr;
//this line causes *str to also be freed.
// free(newstr);
}
int main()
{
char *str;
ft_append(&str, 'x');
ft_append(&str, 'y');
ft_append(&str, 'z');
printf("str = %s\n", str);
}
This code works as intended. printf returns xyz. But I am wondering if it is okay for me to not use free() in ft_append? I always read that when a new array is created using malloc I should follow up with free when I am not using the array anymore, but when I type free(newstr), str also gets free resulting in printf returning nothing.
For starters the program has undefined behavior because the pointer str defined in main was not initialized and has an indeterminate value.
char *str;
So at least calling the function ft_strlen for such a pointer
int len = ft_strlen(*str);
invokes undefined behavior.
Also the function ft_append produces memory leaks because early allocated memory the address of which is stored in the pointer str is not freed in subsequent calls of the function.
You need initially to set the pointer str to NULL
char *str = NULL;
and within the function ft_append to use the function realloc instead of malloc.
The program can look the following way
#include <stdio.h>
#include <stdlib.h>
size_t ft_strlen( const char *str )
{
size_t i = 0;
while ( str[i] ) i++;
return i;
}
int ft_append( char **str, char c )
{
size_t len = *str == NULL ? 0 : ft_strlen( *str );
char *newstr = realloc( *str, len + 2 );
int success = newstr != NULL;
if ( success )
{
newstr[len] = c;
newstr[len + 1] = '\0';
*str = newstr;
}
return success;
}
int main( void )
{
char *str = NULL;
ft_append( &str, 'x' );
ft_append( &str, 'y' );
ft_append( &str, 'z' );
printf( "str = %s\n", str );
free( str );
}
Yes its a problem as your program will have memory leaks , where information you have put in memory will stay there even after your program has exited.
look at a program called Valgrind Website and Install it.
Run valgrind --leak-check=yes nameofyourprogram and it will indicate how much memory is leaked .
The problem is malloc() allocates memory randomly which means that once the memory leak happens its hard to trace and delete unless you restart you computer ,A lot of memory leaks can cause out of memory issues if this was a big project.
In Short
free(newstr);
newstr=NULL;
Also use ((len +2 )* sizeof(char)) Instead so the malloc is char size based
At the end of ft_append the old value of *str must be freed:
if (str2 != NULL) {
free(str2);
}
You must decide whether *str is allowed to be NULL.
But consider doing ft_append twice. then there are three values of *str the first two needing to be freed in ft_append. After calling ft_append, you lose the old *str.
The other issues are already addressed in the older answers.
Related
According to this:
strcpy vs strdup,
strcpy could be implemented with a loop, they used this while(*ptr2++ = *ptr1++). I have tried to do similar:
#include <stdio.h>
#include <stdlib.h>
int main(){
char *des = malloc(10);
for(char *src="abcdef\0";(*des++ = *src++););
printf("%s\n",des);
}
But that prints nothing, and no error. What went wrong?
Thanks a lot for answers, I have played a bit, and decided how best to design the loop to see how the copying is proceeding byte by byte. This seems the best:
#include <stdio.h>
#include <stdlib.h>
int main(){
char *des = malloc(7);
for(char *src="abcdef", *p=des; (*p++=*src++); printf("%s\n",des));
}
In this loop
for(char *src="abcdef\0";(*des++ = *src++););
the destination pointer des is being changed. So after the loop it does not point to the beginning of the copied string.
Pay attention to that the explicit terminating zero character '\0' is redundant in the string literal.
The loop can look the following way
for ( char *src = "abcdef", *p = des; (*p++ = *src++););
And then after the loop
puts( des );
and
free( des );
You could write a separate function similar to strcpy the following way
char * my_strcpy( char *des, const char *src )
{
for ( char *p = des; ( *p++ = *src++ ); );
return des;
}
And call it like
puts( my_strcpy( des, "abcdef" ) )'
free( des );
You are incrementing des so naturally at the end of the cycle it will be pointing past the end of the string, printing it amounts to undefined behavior, you have to bring it back to the beginning of des.
#include <stdio.h>
#include <stdlib.h>
int main(){
int count = 0;
char *des = malloc(10);
if(des == NULL){
return EXIT_FAILURE; //or otherwise handle the error
}
// '\0' is already added by the compiler so you don't need to do it yourself
for(char *src="abcdef";(*des++ = *src++);){
count++; //count the number of increments
}
des -= count + 1; //bring it back to the beginning
printf("%s\n",des);
free(dest); //to free the allocated memory when you're done with it
return EXIT_SUCCESS;
}
Or make a pointer to the beginning of des and print that instead.
#include <stdio.h>
#include <stdlib.h>
int main(){
char *des = malloc(10);
if(des == NULL){
return EXIT_FAILURE; //or otherwise handle the error
}
char *ptr = des;
for(char *src="abcdef";(*des++ = *src++);){} //using {} instead of ;, it's clearer
printf("%s\n",ptr);
free(ptr) // or free(dest); to free the allocated memory when you're done with it
return EXIT_SUCCESS;
}
printf("%s\n",des); is undefined behavior (UB) as it attempts to print starting beyond the end of the string written to allocated memory.
Copy the string
Save the original pointer, check it and free when done.
const char *src = "abcdef\0"; // string literal here has 2 ending `\0`,
char *dest = malloc(strlen(src) + 1); // 7
char *d = dest;
while (*d++ = *src++);
printf("%s\n", dest);
free(dest);
Copy the string literal
const char src[] = "abcdef\0"; // string literal here has 2 ending `\0`,
char *dest = malloc(sizeof src); // 8
for (size_t i = 0; i<sizeof src; i++) {
dest[i] = src[i];
}
printf("%s\n", dest);
free(dest);
You just need to remember the original allocated pointer.
Do not program in main. Use functions.
#include <stdio.h>
#include <stdlib.h>
size_t strSpaceNeedeed(const char *str)
{
const char *wrk = str;
while(*wrk++);
return wrk - str;
}
char *mystrdup(const char *str)
{
char *wrk;
char *dest = malloc(strSpaceNeedeed(str));
if(dest)
{
for(wrk = dest; *wrk++ = *str++;);
}
return dest;
}
int main(){
printf("%s\n", mystrdup("asdfgfd"));
}
or even better
size_t strSpaceNeedeed(const char *str)
{
const char *wrk = str;
while(*wrk++);
return wrk - str;
}
char *mystrcpy(char *dest, const char *src)
{
char *wrk = dest;
while((*wrk++ = *src++)) ;
return dest;
}
char *mystrdup(const char *str)
{
char *wrk;
char *dest = malloc(strSpaceNeedeed(str));
if(dest)
{
mystrcpy(dest, str);
}
return dest;
}
int main(){
printf("%s\n", mystrdup("asdfgfd"));
}
You allocate the destination buffer des and correctly copy the source string into place. But since you are incrementing des for each character you copy, you have moved des from the start of the string to the end. When you go to print the result, you are printing the last byte which is the nil termination, which is empty.
Instead, you need to keep a pointer to the start of the string, as well as having a pointer to each character you copy.
The smallest change from your original source is:
#include <stdio.h>
#include <stdlib.h>
int main(){
char *des = malloc(10);
char *p = des;
for(char *src="abcdef";(*p++ = *src++););
printf("%s\n",des);
}
So p is the pointer to the next destination character, and moves along the string. But the final string that you print is des, from the start of the allocation.
Of course, you should also allocate strlen(src)+1 worth of bytes for des. And it is not necessary to null-terminate a string literal, since that will be done for you by the compiler.
But that prints nothing, and no error. What went wrong?
des does not point to the start of the string anymore after doing (*des++ = *src++). In fact, des is pointing to one element past the NUL character, which terminates the string, thereafter.
Thus, if you want to print the string by using printf("%s\n",des) it invokes undefined behavior.
You need to store the address value of the "start" pointer (pointing at the first char object of the allocated memory chunk) into a temporary "holder" pointer. There are various ways possible.
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *des = malloc(sizeof(char) * 10);
if (!des)
{
fputs("Error at allocation!", stderr);
return 1;
}
char *tmp = des;
for (const char *src = "abcdef"; (*des++ = *src++) ; );
des = temp;
printf("%s\n",des);
free(des);
}
Alternatives:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *des = malloc(sizeof(char) * 10);
if (!des)
{
fputs("Error at allocation!", stderr);
return 1;
}
char *tmp = des;
for (const char *src = "abcdef"; (*des++ = *src++) ; );
printf("%s\n", tmp);
free(tmp);
}
or
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *des = malloc(sizeof(char) * 10);
if (!des)
{
fputs("Error at allocation!", stderr);
return 1;
}
char *tmp = des;
for (const char *src = "abcdef"; (*tmp++ = *src++) ; );
printf("%s\n", des);
free(des);
}
Side notes:
"abcdef\0" - The explicit \0 is not needed. It is appended automatically during translation. Use "abcdef".
Always check the return of memory-management function if the allocation succeeded by checking the returned for a null pointer.
Qualify pointers to string literal by const to avoid unintentional write attempts.
Use sizeof(char) * 10 instead of plain 10 in the call the malloc. This ensures the write size if the type changes.
int main (void) instead of int main (void). The first one is standard-compliant, the second not.
Always free() dynamically allocated memory, since you no longer need the allocated memory. In the example above it would be redundant, but if your program becomes larger and the example is part-focused you should free() the unneeded memory immediately.
char * removeChar(char * str, char c){
int len = strlen(str);
int i = 0;
int j = 0;
char * copy = malloc(sizeof(char) * (len + 1));
while(i < len){
if(str[i] != c){
copy[j] = str[i];
j++;
i++;
}else{
i++;
}
}
if(strcmp(copy, str) != 0){
strcpy(str,copy);
}else{
printf("Error");
}
return copy;
}
int main(int argc, char * argv[]){
char str[] = "Input string";
char * input;
input = removeChar(str,'g');
printf("%s\n", input);
free(input);
return 0;
}
I don't know why every time I try to run it ,it always says uninitialized variable and sticks in the strcpy line and printf line.
Basically this function is to take a string, and a character and removes the that character from the string (because I am learning malloc so that's why I wrote the function like this).
After the while loop do:
copy[j] = '\0';
to NULL-terminate your string; that way it can work with methods coming from <string.h>, which assume that the string is nul-terminated.
PS: One warning you should see is about not returning copy in your function in any case, because now if the condition of the if statement is wrong, your function won't return something valid, so add this:
return copy;
at the end of your function (which is now corrected with your edit).
Other than that, the only warning you should still get are for the unused arguments of main(), nothing else:
prog.c: In function 'main':
prog.c:32:14: warning: unused parameter 'argc' [-Wunused-parameter]
int main(int argc, char * argv[]){
^~~~
prog.c:32:27: warning: unused parameter 'argv' [-Wunused-parameter]
int main(int argc, char * argv[]){
^~~~
While you copy over bytes from str to copy, you don't add a terminating null byte at the end. As a result, strcmp reads past the copied characters into unitialized memory, possibly past the end of the allocated memory block. This invokes undefined behavior.
After your while loop, add a terminating null byte to copy.
Also, you never return a value if the if block at the end is false. You need to return something for that, probably the copied string.
char * removeChar(char * str, char c){
int len = strlen(str);
int i = 0;
int j = 0;
char * copy = malloc(sizeof(char) * (len + 1));
while(i < len){
if(str[i] != c){
copy[j] = str[i];
j++;
i++;
}else{
i++;
}
}
// add terminating null byte
copy[j] = '\0';
if(strcmp(copy, str) != 0){
strcpy(str,copy);
}
// always return copy
return copy;
}
You never initialised input and the some compilers don't notice,
that the the value is never used before the line
input = removeChar(str, 'g');
in your code. So they emit the diagnostic just to be sure.
strcpy(str, copy)
gets stuck in your code, as copy never got a closing 0 byte and
so depends on the nondeterministic content of your memory at the
moment of the allocation of the memory backing copy, how long strcpy
will run and if you get eventually a SIGSEGV (or similar).
strcpy will loop until it finds a 0 byte in your memory.
For starters to remove a character from a string there is no need to create dynamically a character array and then copy this array into the original string.
Either you should write a function that indeed removes the specified character from a string or a function that creates a new string based on the source string excluding the specified character.
It is just a bad design that only confuses users. That is the function is too complicated and uses redundant functions like malloc, strlen, strcmp and strcpy. And in fact it has a side effect that is not obvious. Moreover there is used incorrect type int for the length of a string instead of the type size_t.
As for your function implementation then you forgot to append the terminating zero '\0' to the string built in the dynamically allocated array.
If you indeed want to remove a character from a string then the function can look as it is shown in the demonstrative program.
#include <stdio.h>
char * remove_char(char *s, char c)
{
char *p = s;
while (*p && *p != c) ++p;
for ( char *q = p; *p++; )
{
if (*p != c) *q++ = *p;
}
return s;
}
int main( void )
{
char str[] = "Input string";
puts(str);
puts(remove_char(str, 'g'));
return 0;
}
The program output is
Input string
Input strin
If you are learning the function malloc and want to use it you in any case should try to implement a correct design.
To use malloc you could write a function that creates a new string based on the source string excluding the specified character. For example
#include <stdio.h>
#include <stdlib.h>
char * remove_copy_char(const char *s, char c)
{
size_t n = 0;
for (const char *p = s; *p; ++p)
{
if (*p != c) ++n;
}
char *result = malloc(n + 1);
if (result)
{
char *q = result;
for (; *s; ++s)
{
if (*s != c) *q++ = *s;
}
*q = '\0';
}
return result;
}
int main( void )
{
char *str = "Input string";
puts(str);
char *p = remove_copy_char(str, 'g');
if ( p ) puts(p );
free(p);
return 0;
}
The program output will be the same as above.
Input string
Input strin
Pay attention to the function declaration
char * remove_copy_char(const char *s, char c);
^^^^^^
In this case the source string can be a string literal.
char *str = "Input string";
i've this code:
int i =0;
char * str = "ar bitrary whitespace";
int whitespace=0,index;
for(index = 0;index < strlen(str);index++)
{
if(isspace(str[index]) != 0)
{
whitespace++;
}
}
char * tmp = (char *)calloc(strlen(str)-whitespace +1,sizeof(char));
memset(tmp,'\0',strlen(tmp)+1);
while(i < strlen(str))
{
if(isspace(str[i]) != 0)
{
i++;
continue;
}else if(isspace(str[i]) == 0)
{
strcat(tmp,&str[i]);
i++;
}
}
printf("\nnew string is: %s \n",tmp);
the problem is that the output is a string without the whitespace removed + some garbage character.
I've used memset to null terminate tmp,is there the problem?
The length of the source string could be calculated before this loop
for(index = 0;index < strlen(str);index++)
Otherwise if the code will not be optimized the function strlen will be called for each iteration of the loop. In fact using of the function is redundant for such a task.
This statement
memset(tmp,'\0',strlen(tmp)+1);
does not make sense because the call of calloc already initialized the memory with zeroes.
This statement
strcat(tmp,&str[i]);
also copies blanks from the source string after the position i. So it can write beyond the memory allocated for the array pointed to by the pointer tmp.
You can write a separate function that can look as it is shown in this demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
char * remove_blanks( const char *s )
{
size_t n = 0;
const char *p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) ++n;
} while ( *p++ );
char *t = malloc( n );
if ( t )
{
char *q = t;
p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) *q++ = *p;
} while ( *p++ );
}
return t;
}
int main(void)
{
char * str = "ar bitrary whitespace";
printf( "\"%s\"\n", str );
char *t = remove_blanks( str );
printf( "\"%s\"\n", t );
free( t );
}
The program output is
"ar bitrary whitespace"
"arbitrarywhitespace"
this is your problem
memset(tmp,'\0',strlen(tmp)+1);
strlen(tmp) works by looking for '\0' in tmp, you have a chicken and egg situation here.
You should not be doing a memset any way, just tack on a '\0' when you fnish copying
And dont use strcat, instead maintain a pointer to tmp and just do *p = str[i] then increment p
I will not read your question, you overwrite the '\0' terminator for sure.
Now that I read your question, it looks like you need to understand strings and arrays better,
Don't ever write while (i < strlen(str))
Don't use strcat() for adding a single character, you apparently did overwrite the '\0' there. Furthermore, don't ever use strcat() for concatenating more than to pieces of a string.
Also notable,
You memset() after calloc() which already initialized to 0. That means that you are enforcing something that is not necessary, and trying it twice as if it failed the first time which I can guarantee it didn't.
In fact, since you have used calloc() and all bytes pointed to by tmp are 0 then strlen(tmp) will return 0, thus your memset() is equivalent to
tmp[0] = '\0';
and you REALLY don't need initialize tmp except when you finally copy the actual bytes from str.
I always advice against calloc() for strings, because
You don't really need to initialize something twice.
You should be sure your code does take the terminating '\0' into account and not simply assume that it's there because you calloc()ed. That is a bug that you just hide with calloc() but it shows up at some point.
Try this and see if you can understand the reasons for my changes
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(void)
{
int whitespace;
int length;
char *str = "ar bitrary whitespace";
char *tmp;
whitespace = 0;
for (length = 0; str[length] != '\0'; ++length) {
if (isspace(str[length]) != 0) {
whitespace++;
}
}
tmp = malloc(length - whitespace + 1);
if (tmp == NULL)
return -1;
for (int i = 0, j = 0; str[i] != '\0'; ++i) {
if (isspace(str[i]) != 0)
continue;
tmp[j++] = str[i];
}
tmp[length - whitespace] = '\0';
printf("new string is: %s\n",tmp);
free(tmp);
return 0;
}
I'm fairly new to C and I'm trying to work out dynamic memory allocation for reading from a file. At least I think that's what I'm doing.
Anyway, this code works:
int readfromfile(FILE *filepointer)
{
size_t size = 2;
char *str = (char *) malloc(sizeof(char));
int character = 0;
size_t counter = 0;
while((character = fgetc(filepointer)) != EOF)
{
str = (char *) realloc(str, size);
str[counter] = (char) character;
size ++;
counter++;
}
str[counter] = '\0';
printf("+%s+", str);
free(str);
return 0;
}
And this code does not:
int main()
{
char *str = (char *) malloc(sizeof(char));
...
readfromfile(ifpointer, &str);
}
int readfromfile(FILE *filepointer, char **str)
{
size_t size = 2;
int character = 0;
size_t counter = 0;
while((character = fgetc(filepointer)) != EOF)
{
*str = (char *) realloc(*str, size);
*str[counter] = (char) character;
size ++;
counter++;
}
str[counter] = '\0';
printf("+%s+", *str);
free(str);
return 0;
}
I cannot understand why because as far as I understand I'm sending a pointer to the location of the char array to the function and accessing the data everytime. The compilers shows no error messages, it just loops through once and on the second loop crashes after the realloc every time. The character assigned to the first value is garbage too.
I have spent an age trying to get this to worked and done a lot of research so I apologise if I've missed a solution but I'm truly stuck at this point.
You get a crash because
*str[counter] = (char) character;
is the same as
*(str[counter]) = (char) character;
as opposed to
(*str)[counter] = (char) character;
which is actually what you wanted. Read Operator Precedence on Wikipedia. You'll find that [] has more precedence than the * (dereference operator).
Also, the cast here, as well as in the calls to realloc and malloc, is unnecessary. Don't forget to check the return value of realloc, malloc etc to see if they were successful in allocating memory.
Now, you have another problem: free(str); in the second code should be free(*str);. Note that after *str has been freed from the function, you aren't supposed to read or write into this memory location from main as it has now become invalid for you to tamper with.
in your int readfromfile(FILE *filepointer, char **str) the parameter char **str is actually the same as char *str[], which means **str is expecting an array of char pointers. however you're passing to it char *str which is just an array of char
when you use readfromfile(...) you should do it this way (something like...):
char *str[2] = {"some char array", "another char array"};
readfromfile(ifpointer, str);
or:
char *a = "this char array";
char **str = &a[0];
readfromfile(ifpointer, str);
you'll get the idea...
I've written my own getline function following K&R c book
void getline(char * const str)
{
int c;
char* temp = str;
while ((c=getchar()) != '\n') {
*temp = c;
temp++;
}
*temp = '\0'
}
and it's used to initialize strings
char *str1, *str2;
printf("Type string 1: ");
getline(str1);
printf("Type string 2: ");
getline(str2);
Just wonder that, what if the memory locations str1 and str1 point to are very close, then getline(str2) overwrites contents in string 1?
It that's possible how could I avoid it? THANKS!
Update:
Yes the program stops executing the above code snippet but the code below works:
#include <stdio.h>
main()
{
char* str;
char* temp = str;
int c;
while ((c=getchar()) != '\n') {
*temp = c;
++temp;
}
*temp = '\0';
printf("%s\n", str);
}
Here the str is also uninitialized character pointer but why deoesn't it give me an error?
What you have is Undefined Behavior.
Explanation:
You declared two pointers to char:
char *str1, *str2;
but you haven't initialized them. They point to some "random" memory location as they are uninitialized.
Then, you pass str1 and str2 to getline and here:
char* temp = str;
temp points to where str points to. Then, in the loop,
*temp = c;
you write to this memory location. This writes into an invalid memory location. And invokes UB.
Fix:
You can use an automatic array with a fixed size:
char str1[101], str2[101];
Note that you should add a check in the loop in the getline function which breaks the loop when the user has entered 100 characters so that there won't be a buffer overflow.
A better solution would be to use dynamic memory allocation. You need to use malloc and realloc for that. These functions require the stdlib.h header.
Fixed code (untested):
char* getline()
{
char* str;
int c, size = 10, counter = 0;
str = malloc(size); /* Allocate `size` memory */
if(str == NULL)
{
printf("malloc failed to allocate memory");
exit(-1); /* Exit the program */
/* Or
return NULL;
*/
}
while ((c = getchar()) != '\n' && c != EOF) { /* Added check for EOF as well */
str[counter] = c;
counter++;
if(counter == size)
{
char* temp = str; /* Backup in case realloc fails */
size += 10; /* Which is the same as `size = size + 10` */
str = realloc(str, size); /* realloc size(20) memory */
if(str == NULL) /* If realloc failed */
{
printf("reallocing memory failed");
str = temp; /* str is NULL, retrieve the original contents */
break; /* Break out of the loop */
}
}
}
str = realloc(str, counter + 1); /* realloc `counter + 1` memory */
str[counter] = '\0';
return str;
}
and in the calling function,
char* str1 = getline();
if(str1)
puts(str1);
free(str1);
char* str2 = getline();
if(str2)
puts(str2);
free(str2);
str1 and str2 are not initialized thus it'll be undefined behaviour. You can access to not allowed memory which will crash your program.
You have to allocate enough memory for each pointer and pass its size to get line function to ensure you write only in allocated memory.
str1 and str2 are uninitialized.Right now you write (*temp = c;) at invalid (or unauthorized) memory location invoking UB .
First you need to allocate memory to str1 and str2 .
str1=malloc(100); // check return
str2=malloc(100);
To be able to write into that memory location.
Just wonder that, what if the memory locations str1 and str1 point to are very close, then getline(str2) overwrites contents in string 1?It that's possible how could I avoid it?
And as far as you concern , memory allocated by these malloc won't overlap (will be two different contagious memory blocks) , so if you also tend to write beyond these memory location ,you will invoke undefined behaviour (if you are lucky segmentation fault ) . So, IMHO, there won't be any case of str2 over-writing str1.