How to properly implement strcpy in c? - c

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.

Related

Completing a function using pointers

one of the assignments in my class has this objective:
Complete CapVowels(), which takes a string as a parameter and returns a new string containing the string parameter with the first occurrence of each of the five English vowels (a, e, i, o, and u) capitalized.
Hint: Begin CapVowels() by copying the string parameter to a newly allocated string.
Ex: If the input is:
management
the output is:
Original: management
Modified: mAnagEment
This is the current code I have, and I will highlight the section I'm supposed to complete:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
**// Return a newly allocated copy of original
// with the first occurrence of each vowel capitalized
char* CapVowels(char* original) {
return CapVowels(*original= "A.E,I,O,U");
}**
int main(void) {
char userCaption[50];
char* resultStr;
scanf("%s", userCaption);
resultStr = CapVowels(userCaption);
printf("Original: %s\n", userCaption);
printf("Modified: %s\n", resultStr);
// Always free dynamically allocated memory when no longer needed
free(resultStr);
return 0;
}
The section with the ** meaning it's bolded is the section I'm supposed to complete before the int main(void). I can't figure out how to complete the objective. I get mixed up with pointers and dereferencing and, I tried dereferencing when returning the function so that the value will come out to what it's supposed to. I understand one part of it, but I don't know how you would complete it to output to the required output:
Original: management
Modified: mAnagEment
Hint: Begin CapVowels() by copying the string parameter to a newly allocated string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Return a newly allocated copy of original
// with the first occurrence of each vowel capitalized
char* CapVowels(const char* original) {
char* result = strcpy(malloc(strlen(original+1)), original);
char* vowels = "aeiou";
while(*vowels)
{
char* ptr = strchr(result, *vowels);
(ptr)? *ptr = toupper(*ptr) : vowels++;
}
return result;
}
int main(void) {
char userCaption[50];
char* resultStr;
scanf("%s", userCaption);
resultStr = CapVowels(userCaption);
printf("Original: %s\n", userCaption);
printf("Modified: %s\n", resultStr);
// Always free dynamically allocated memory when no longer needed
free(resultStr);
return 0;
}
Output
Success #stdin #stdout 0s 5424KB
Original: management
Modified: mAnAgEmEnt
You can use strlen to get the length of the input, then use malloc to allocate enough space for the result. Then, just loop over the input until the terminating null character ('\0'), incrementally assigning the current character to the result if it is a consonant or the uppercase version if it is a vowel (using the toupper function).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *CapVowels(char *original){
if (!original)
return NULL;
const char *vowels = "aeiou";
size_t len = strlen(original); // get length of input (terminating '\0' not included)
char *result = malloc(len + 1); // allocate memory for new string (note that sizeof(char) is 1)
for (char *dest = result; *original; ++original)
*dest++ = strchr(vowels, *original) // check if current character is in the vowels
? toupper(*original) : *original;
result[len] = '\0';
return result;
}
Here's a version that works even with multiple words in 'original'.
char *CapVowels( const char *original ) {
char *cp, *out = strdup( original );
for( char *vowels = "aeiou"; *vowels; vowels++ ) {
if( ( cp = strchr( out, *vowels ) ) != NULL )
*cp = toupper( *cp );
}
return out;
}
void main( void ) {
char userCaption[50];
gets( userCaption );
char *capped = CapVowels( userCaption );
printf( "Original: %s\n", userCaption );
printf( "Modified: %s\n", capped );
free( capped );
}

This code works without free() involved. Is this okay?

#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.

Segmentation fault on strcat

I have recently begun working on learning the C language and have repeatedly run into an error in which calling the strcat function from the <string.h> module results in a segmentation fault. I've searched for the answers online, including on this stackoverflow post, without success. I thought this community might have a more personal insight into the problem, as the general solutions don't seem to be working. Might be user error, might be a personal issue with the code. Take a look.
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
char *buffer = malloc(strlen(new)+1);
for (int i=0; i<strlen(*str); i++){
if(buffer!=NULL){
if(str[i]!=" "){
strcat(new,str[i]); //Segmentation fault
}
}
}
free(buffer);
return new;
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
puts(new);
}
I've placed a comment on the line I've traced the segmentation fault back to. The following is some Java to make some sense out of this C code.
public class deblank {
public static void main(String[]args){
String str = "This has space in it.";
System.out.println(removeBlanks(str));
}
public static String removeBlanks(String str){
String updated = "";
for(int i=0; i<str.length(); i++){
if(str.charAt(i)!=' '){
updated+=str.charAt(i);
}
}
return updated;
}
}
Any insights into this error will be much appreciated. Please point out typos as well... I've been known to make them. Thanks.
OK, let's do this.
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
^ This line creates an array of pointers, not a string.
char *buffer = malloc(strlen(new)+1);
malloc is undeclared. Missing #include <stdlib.h>. Also, you should check for allocation failure here.
strlen(new) is a type error. strlen takes a char * but new is (or rather evaluates to) a char **.
for (int i=0; i<strlen(*str); i++){
strlen(*str) is a type error. strlen takes a char * but *str is a char (i.e. a single character).
i<strlen(...) is questionable. strlen returns size_t (an unsigned type) whereas i is an int (signed, and possibly too small).
Calling strlen in a loop is inefficient because it has to walk the whole string to find the end.
if(buffer!=NULL){
This is a weird place to check for allocation failure. Also, you don't use buffer anywhere, so why create/check it at all?
if(str[i]!=" "){
str[i]!=" " is a type error. str[i] is a char whereas " " is (or rather evaluates to) a char *.
strcat(new,str[i]); //Segmentation fault
This is a type error. strcat takes two strings (char *), but new is a char ** and str[i] is a char. Also, the first argument to strcat must be a valid string but new is uninitialized.
}
}
}
free(buffer);
return new;
new is a local array in this function. You're returning the address of its first element, which makes no sense: As soon as the function returns, all of its local variables are gone. You're returning an invalid pointer here.
Also, this is a type error: deblank is declared to return a char * but actually returns a char **.
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
This is a type error: *new is a char but deblank returns a char *.
puts(new);
puts takes a string, but new is essentially garbage at this point.
}
You can't use strcat like you did, it is intended to catenate a C-string at the end of another given one. str[i] is a char not a C-string (remember that a C-string is a contiguous sequence of chars the last being the NUL byte).
You also cannot compare strings with standard comparison operators, if you really need to compare strings then there is a strcmp function for it. But you can compare chars with standard operators as char is just a kind of integer type.
This should do the trick:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * deblank(const char str[]) {
char *buffer = malloc(strlen(str)+1); // allocate space to contains as much char as in str, included ending NUL byte
for (int i=0, j=0; i<strlen(str)+1; i++) { // for every char in str, included the ending NUL byte
if (str[i]!=' ') { // if not blank
buffer[j++] = str[i]; // copy
}
}
return buffer; // return a newly constructed C-string
}
int main(void){
char str[] = "This has spaces in it.";
char *new = deblank(str);
puts(new);
free(new); // release the allocated memory
}
So, not sure whether this helps you, but a C code doing the same as your Java code would look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *removeBlanks(const char *str)
{
char *result = malloc(strlen(str) + 1);
if (!result) exit(1);
const char *r = str;
char *w = result;
while (*r)
{
// copy each character except when it's a blank
if (*r != ' ') *w++ = *r;
++r;
}
*w = 0; // terminate the result to be a string (0 byte)
return result;
}
int main(void)
{
const char *str = "This has spaces in it.";
char *new = removeBlanks(str);
puts(new);
free(new);
return 0;
}
I would'nt recommend to name a variable new ... if you ever want to use C++, this is a reserved keyword.
I tried compiling with warnings enabled, here are some you should fix.
You need to include stdlib.h
char *new[strlen(str)] creates an array of char* not of char, so not really a string. Change it to char new[strlen(str)].
To check if str[i] is a space, you compare it to the space character ' ', not a string whose only character is a space " ". So change it to str[i]!=' '
strcat takes a string as the second argument and not a character, like you're giving it with str[i].
Also, what are you using buffer for?
Another mistake, is that you probably assumed that uninitialized arrays take zero values. The new array has random values, not zero/null. strcat concatenates two strings, so it would try to put the string in its second argument at the end of the first argument new. The "end" of a string is the null character. The program searches new for the first null character it can find, and when it finds this null, it starts writing the second argument from there.
But because new is uninitialized, the program might not find a null character in new, and it would keep searching further than the length of new, strlen(str), continuing the search in unallocated memory. That is probably what causes the segmentation fault.
There can be three approaches to the task.
The first one is to update the string "in place". In this case the function can look something like the following way
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char s[] )
{
size_t i = 0;
while ( s[i] and not isblank( s[i] ) ) ++i;
if ( s[i] )
{
size_t j = i++;
do
{
if ( not isblank( s[i] ) ) s[j++] = s[i];
} while( s[i++] );
}
return s;
}
int main(void)
{
char s[] = "This has spaces in it.";
puts( s );
puts( deblank( s ) );
return 0;
}
The program output is
This has spaces in it.
Thishasspacesinit.
Another approach is to copy the source string in a destination character array skipping blanks.
In this case the function will have two parameters: the source array and the destination array. And the size of the destination array must be equal to the size of the source array because in general the source array can not have blanks.
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char *s1, const char *s2 )
{
char *t = s1;
do
{
if ( not isblank( *s2 ) ) *t++ = *s2;
} while ( *s2++ );
return s1;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char s2[sizeof( s1 )];
puts( s1 );
puts( deblank( s2, s1 ) );
return 0;
}
The program output will be the same as shown above.
Pay attention to this declaration
char s2[sizeof( s1 )];
The size of the destination string in general should be not less than the size of the source string.
And at last the third approach is when inside the function there is created dynamically an array and pointer to the first element of the array is returned from the function.
In this case it is desirable at first to count the number of blanks in the source array that to allocated the destination array with the appropriate size.
To use the functions malloc and free you need to include the following header
#include <stdlib.h>
The function can be implemented as it is shown in the demonstrative program.
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <iso646.h>
char * deblank( const char *s )
{
size_t n = 1; /* one byte reserved for the terminating zero character */
for ( const char *t = s; *t; ++t )
{
if ( not isblank( *t ) ) ++n;
}
char *s2 = malloc( n );
if ( s2 != NULL )
{
char *t = s2;
do
{
if ( not isblank( *s ) ) *t++ = *s;
} while ( *s++ );
}
return s2;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char *s2 = deblank( s1 );
puts( s1 );
if ( s2 ) puts( s2 );
free( s2 );
return 0;
}
The program output is the same as for the two previous programs.
As for the standard C function strcat then it cats two strings.
For example
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
puts( strcat( s1, s2 ) );
return 0;
}
The destination array (in this case s1) must have enough space to be able to append a string.
There is another C function strncat in the C Standard that allows to append a single character to a string. For example the above program can be rewritten the following way
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
for ( size_t i = 0; s2[i] != '\0'; i++ )
{
strncat( s1, &s2[i], 1 );
}
puts( s1 );
return 0;
}
But it is not efficient to use such an approach for your original task because each time when the function is called it has to find the terminating zero in the source string that to append a character.
you can try recursively
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void deblank(const char* str, char *dest) {
if (!*str) {*dest = '\0';return;}
// when we encounter a space we skip
if (*str == ' ') {
deblank(str+1, dest);
return;
}
*dest = *str;
deblank(str+1, dest+1);
}
int main(void) {
const char *str = "This has spaces in it.";
char *output = malloc(strlen(str)+1);
deblank(str, output);
puts(output);
free(output);
}

Why malloc can't work with strcpy?

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";

Segmentation Fault while trying to copy string in C function

I have a function in 'C' that is supposed to implement my own strcpy program. Here's what I wrote. However, I am not able to debug the cause of Segmentation Fault.
#include <stdio.h>
#include <stdlib.h>
char * mystrcpy(char *dest, char *src) {
char * ptr = dest;
while (*src != '\0'){
*ptr = *src;
ptr++; src++;
//printf("Dest is \"%s\" and Source is \"%s\"\n",dest,src);
}
*ptr = '\0';
return dest;
}
int main() {
char str[] = "I rock always";
char * dest = NULL;
dest = mystrcpy(dest, str);
printf("Source String %s and Destination String %s\n", str, dest);
}
Can someone explain this behavior to me ?
You have to allocate memory for the destination string:
int main() {
char str[] = "I rock always";
char * dest = (char*)malloc(strlen(str) + 1);
dest = mystrcpy(dest, str);
printf("Source String %s and Destination String %s\n", str, dest);
}
Of course, it is good manners to free the memory eventually:
free(dest);
You never allocate any memory for *dest to point to.
It starts pointing to NULL, and then you attempt: *ptr = *src.
#include <stdio.h>
/* this the K&R version of strcpy */
char * mystrcpy(char *dest, char *src) {
char *ptr = dest;
while ( *ptr++ = *src++ ) {;}
return dest;
}
int main(void) {
char str[] = "I rock always";
char dest[sizeof str];
char *result;
result = mystrcpy(dest, str);
printf("Source String %s and Destination String %s and result %s\n", str, dest, result);
return 0;
}
Allocate memory and try....
#include <stdio.h>
#include <stdlib.h>
char * mystrcpy(char *dest, char *src)
{
char * ptr = dest;
int index=0;
while (*src != '\0')
{
*(ptr+index) = *(src+index);
index++;
}
*(ptr+index) = '\0';
return dest;
}
int main()
{
char str[] = "I rock always";
char * dest = (char*)malloc((strlen(str)+1)*sizeof(char));
dest = mystrcpy(dest, str);
printf("Source String %s and Destination String %s\n", str, dest);
free(dest);
return 0;
}
You must allocate memory for the destination string buffer.
Memory management is a very big part of C ... you need to be very careful that you allocate AND deallocate memory as it is used and becomes surplus to requirements. Failure to do so will result in segmentation faults (failing to allocate memory) and memory leaks (failing to free memory)
Other answers here give you examples of malloc so I won't repeat them here.
You should use the functions already available to you as much as possible, rather than writing your own. This avoids errors in implementation as somebody has already debugged and optimized it for you.
http://www.cs.cf.ac.uk/Dave/C/node19.html

Resources