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);
}
Related
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 );
}
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.
I get segmentation fault when using char *s in main. If I use char s[100] or something like that everything is ok. Why is that? SIGSEGV appears when i call find_short(char *s) function on line with instruction char *token = strtok(s, delim);. This is my code:
#include <sys/types.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
int find_short(char *s)
{
int min = INT_MAX;
const char delim[2] = " ";
char *token = strtok(s, delim);
while(token != NULL) {
int len = (int)strlen(token);
if (min > len)
min = len;
token = strtok(NULL, delim);
}
return min;
}
int main()
{
char *s = "lel qwew dasdqew";
printf("%d",find_short(s));
return 0;
}
The line:
char *s = "lel qwew dasdqew";
creates a pointer to a constant string in memory.
Because that string is constant, you are unable to change its contents.
The strtok function will try to modify the contents by inserting \0 at the token-delimiter locations, and will fail because the string cannot be modified.
Changing the line to:
char s[] = "lel qwew dasdqew";
Now makes s an array of local data that you are free to change. strtok will now work because it can change the array.
The main your mistake is that you selected a wrong function to do the task.:)
I will say about this below.
As for the current program then string literals in C though they do not have constant character array types are immutable. Any attempt to change a string literal results in undefined behavior. And the function strtok changes passed to it string inserting the terminating zero between sub-strings.
Instead of the function strtok you should use string functions strspn and strcspn. They do not change the passed argument. So using these functions you are able to process also string literals.
Here is a demonstrative program.
#include <stdio.h>
#include <string.h>
size_t find_short( const char *s )
{
const char *delim= " \t";
size_t shortest = 0;
while ( *s )
{
s += strspn( s, delim );
const char *p = s;
s += strcspn( s, delim );
size_t n = s - p;
if ( shortest == 0 || ( n && n < shortest ) ) shortest = n;
}
return shortest;
}
int main(void)
{
const char *s = "lel qwew dasdqew";
printf( "%zu", find_short( s ) );
return 0;
}
Its output is
3
This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Why isn't the size of an array parameter the same as within main?
(13 answers)
Closed 3 years ago.
In this part of my code I remove white spaces of string1 and copy the result to string2.
char * remove_blank_spaces(char * string1) {
char * string2 = malloc(sizeof(string1));
int index = 0;
for (int i = 0; string1[i] != 0; i++) {
if(string1[i] != ' ') {
//printf("i: %d\n", i);
//printf("c2: %c\n", string1[i]);
string2[index] = string1[i];
index++;
}
}
string2[index] = '\0';
printf("string2: %s\n", string2);
return string2;
}
I check the result with:
assert(remove_blank_spaces("a b") == "ab"); // Edit: here is the error!
I got an error: Assertion failed! and Expression: remove_blank_spaces("a b") == "ab"
I compared the strings in Virtual-C and they look the same.
Why the assertion is failing?
Your code has a bug: malloc allocates insufficient space, and this results in undefined behaviour when trying to access unallocated memory.
The assertion is also failing because you are comparing pointers via ==, instead of C strings via strcmp.
Furthermore, I suggest making two changes:
Don’t mix computation and output. Return the value, don’t printf it inside the function.
Use descriptive and correct names. This requires taking context into account. For instance, index can generally be a good name, but in your case it’s unclear which index you’re referring to, and this invites errors, where index is used to index into the wrong variable. As for “correct” names, what you call “blank space” is more conventionally known as “whitespace”.
To improve the second point, I suggest actually changing the implementation and, instead of having a second index variable, to iterate over the output using a pointer. There are other possibilities, but this one has the advantage that accidentally indexing using the wrong variable is impossible.
Taking this together, we get
char *remove_whitespace(const char *str) {
char *result = malloc(strlen(str) + 1);
char *out = result;
for (size_t i = 0; str[i] != '\0'; i++) {
if (str[i] != ' ') {
*out++ = str[i];
}
}
*out = '\0';
return result;
}
We could additionally do away with the i loop counter. Unfortunately the result is less readable, not more, because we would need to increment str at the end of the loop, and this would leave us with an unsightly for (; *str != '\0'; str++) loop construct.
For starters this function declaration
char * remove_blank_spaces(char * string1) {
is incorrect and only confuses users of the function. If within the function you are creating a new character array then the parameter shall have the qualifier const.
char * remove_blank_spaces( const char * string1) {
Otherwise the function should change the original string "in-place".
This call
char * string2 = malloc(sizeof(string1));
also is incorrect. I think you mean
char * string2 = malloc( strlen( string1 ) + 1 );
But even this call is not very good because the result string can be much less than the original string.
So at first you should count the numb er of characters in the result string and only then allocate the memory.
This assert is also incorrect
assert(remove_blank_spaces("a b") == "ab");
In this expression there are compared addresses of two string: the first one is the string returned by the function and the second one is the string literal.
Even if you will write an expression like this
assert( "ab" == "ab");
the value of the expression can be equal either to logical true or false depending on the compiler option that specifies whether equal string literals are stored as one string literal or occupy different extents of memory.
You should write instead
assert( strcmp( remove_blank_spaces("a b"), "ab" ) == 0 );
Take into account that it is reasonable also to consider trhe tab character '\t' in the if statement like
if(string1[i] != ' ' && string1[i] != '\t') {
Or you could use the standard function isblank.
Here is a demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
char * remove_blank_spaces( const char *s )
{
size_t n = 0;
for ( size_t i = 0; s[i] != '\0'; i++ )
{
if ( !isblank( ( unsigned char )s[i] ) ) ++n;
}
char *result = malloc( n + sizeof( ( char )'\0' ) );
char *p = result;
do
{
if ( !isblank( ( unsigned char )*s ) )
{
*p++ = *s;
}
} while ( *s++ != '\0' );
return result;
}
int main(void)
{
const char *s1 = "a b";
char *s2 = remove_blank_spaces( s1 );
assert( strcmp( s1, s2 ) == 0 );
puts( s2 );
free( s2 );
return 0;
}
The program output is
ab
Pay attention to that instead of the type int as it is shown in other answers you should use the type size_t for the variables index and i because it is the type that is used with string lengths and indices and by the function malloc. The type int is not large enough to store size of strings.
If you indeed want to declare the function like
char * remove_blank_spaces( char *s )
that is when the parameter does not have the qualifier const then you shall not allocate dynamically a new character array within the function and the function itself can look much simpler.
Here is a demonstrative program.
#include <stdio.h>
#include <assert.h>
#include <string.h>
char * remove_blank_spaces( char *s )
{
char *destination = s;
char *source = s;
do
{
if ( *source != ' ' && *source != '\t' )
{
*destination++ = *source;
}
} while ( *source++ != '\0' );
return s;
}
int main(void)
{
char s[] = "a b";
remove_blank_spaces( s );
assert( strcmp( s, "ab" ) == 0 );
puts( s );
return 0;
}
Its output is
ab
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";