how can I use strncat with heap objects?
Im trying to write a simple function to concatenate 2 strings together returning the result, however, I can not run it without making the return buffer very large (adding approximately an additional 5000 to its length) for it to not overflow.
I'm probably just using the strncat function incorrectly using heap objects instead of character arrays of fixed length. but I don't know how I would write it any other way.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *concatStrings(char *arg1, char *arg2) {
char *ret = malloc(strlen(arg1) + strlen(arg2));
strncpy(ret, arg1, strlen(arg1));
strncat(ret, arg2, strlen(arg2));
ret[strlen(arg1)+strlen(arg2)] = '\0';
return ret;
}
int main(int argc, char *argv[]) {
if (argc == 3) {
char *print = concatStrings(argv[1], argv[2]);
printf("\n%s", print);
free(print);
}
return 0;
}
For starters the function should be declared like
char * concatStrings( const char* arg1, const char* arg2 );
because the strings pointed to by the pointers arg1 and arg2 are not being changed within the function.
In this memory allocation
char *ret = malloc(strlen(arg1) + strlen(arg2));
you forgot to reserve memory for the null terminating character '\0'. You have to write
char *ret = malloc( strlen(arg1) + strlen(arg2) + 1 );
Using the magic number 10 in this call
strncpy(ret,arg1,10);
does not make a sense.
If instead you will write for example
strncpy(ret,arg1,strlen(arg1));
then the next call
strncat(ret,arg2,strlen(arg2));
will invoke undefined behavior because the call strncpy did not append the null terminating character '\0' to the string pointed to by the pointer ret.
It would be much better just to write at least
strcpy( ret, arg1 );
In any case your function implementation is inefficient. For example there are two times called the function strlen for the parameter arg2
char *ret = malloc(strlen(arg1) + strlen(arg2));
//...
strncat(ret,arg2,strlen(arg2));
Also the call of strncat is also inefficient because the function needs to traverse the whole target string to find its terminating zero.
The function can be defined the following way
char * concatStrings( const char* arg1, const char* arg2 )
{
size_t n1 = strlen( arg1 );
char *ret = malloc( n1 + strlen( arg2 ) + 1 );
if ( ret != NULL )
{
strcpy( ret, arg1 );
strcpy( ret + n1, arg2 );
}
return ret;
}
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * concatStrings( const char* arg1, const char* arg2 )
{
size_t n1 = strlen( arg1 );
char *ret = malloc( n1 + strlen( arg2 ) + 1 );
if ( ret != NULL )
{
strcpy( ret, arg1 );
strcpy( ret + n1, arg2 );
}
return ret;
}
int main(void)
{
const char *argv1 = "Hello ";
const char *argv2 = "World!";
char *print = concatStrings( argv1, argv2 );
if ( print != NULL ) puts( print );
free( print );
return 0;
}
The program output is
Hello World!
It would be even better to substitute the first call of strcpy for memcpy within the function. That is the function can also look like
char * concatStrings( const char* arg1, const char* arg2 )
{
size_t n1 = strlen( arg1 );
char *ret = malloc( n1 + strlen( arg2 ) + 1 );
if ( ret != NULL )
{
memcpy( ret, arg1, n1 );
strcpy( ret + n1, arg2 );
}
return ret;
}
The first problem is, that you don't allocate enough memory, because in C a string is terminated by a 0-byte so it should be
char *ret = malloc((strlen(arg1) + strlen(arg2))+1);
And of couse you should check if malloc worked.
if (!ret)
// error
The second problem is, that you use strncpy with a hardcoded length here.
Just use strcpy because you already allocated enough memory.
You can also use strcat for the same reason. strncat doesn't provide any additionaly benefit, and in fact makes the code slower because you, again, call strlen.
This
ret[10 + strlen(arg2)] = '\0';
is not needed at all. In fact its potentially dangerous if arg1 is less then 10 characters.
I forget snprintf existed and thought I had to use either strcpy strcat or some other string or memory management function to do this
char* concatStrings(char* arg1, char*arg2){
size_t n1 = strlen(arg1), n2 = strlen(arg2);
char *ret = malloc(n1 + n2 + 1);
snprintf(ret,n1 + n2 + 1, "%s%s", arg1, arg2);
return ret;
}
Problems
Not allocating space for the null character. Off by 1.
strncpy(ret, arg1, strlen(arg1)) does not null character terminate ret, so the next strncat(ret,... is undefined behavior as ret needs to point to a string.
Some alternatives:
// Via sprintf
// Good compilers will emit efficient code
// and not perform the usual parsing overhead of *printf format at run time
char *concatStrings_alt1(const char *arg1, const char *arg2) {
char *ret = malloc(strlen(arg1) + strlen(arg2) + 1);
sprintf(ret, "%s%s", arg1, arg2);
return ret;
}
// Via memcpy
char *concatStrings_alt2(const char *arg1, const char *arg2) {
size_t len1 = strlen(arg1);
size_t size2 = strlen(arg2) + 1;
char *ret = malloc(len1 + size2);
memcpy(ret, arg1, len1);
memcpy(ret + len1, arg2, size2);
return ret;
}
Related
hi i'm trying to write a function that get 3 words, concatenate them to one word and print the word reverse.
i can't put the concatenated word into the ptr w1 in concatWords function.
another question - should i put '\0' in the end of every word?
can someone review my code?
thanks!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 10
void initWords(char *w1, char *w2, char *w3) {
printf("Enter first word:\n");
scanf("%10s", w1);
printf("Enter second word:\n");
scanf("%10s", w2);
printf("Enter third word:\n");
scanf("%10s", w3);
}
char* concatWords(char *w1, char *w2, char *w3) {
int len = strlen(w1) + strlen(w2) + strlen(w3);
w1 = (char*)malloc(sizeof(char) * len);
strcat(w1, w2);
strcat(w1, w3);
return w1;
}
void printReversedWord(char *word) {
int len = strlen(word);
for (int i = len - 1; i >=0; i--) {
printf("%c", word);
}
}
int main()
{
char word1[N+1];
char word2[N+1];
char word3[N+1];
char* longWord;
initWords(word1, word2, word3);
longWord = concatWords(word1, word2, word3);
printReversedWord(longWord);
free(longWord);
return 0;
}
You allocate memory that you assign to w1. Whatever w1 was pointing at can therefore not be included in the final string. You need a separate variable for where to put the final result.
Example:
char *concatWords(char *w1, char *w2, char *w3) {
size_t w1len = strlen(w1);
size_t w2len = strlen(w2);
size_t w3len = strlen(w3);
// the final result will be stored in memory pointed out by `res`:
char *res = malloc(w1len + w2len + w3len + 1); // +1 for null terminator
if (res) {
// copy the three strings where they belong:
memcpy(res, w1, w1len);
memcpy(res + w1len, w2, w2len);
memcpy(res + w1len + w2len, w3, w3len + 1); // +1 for null terminator
}
return res;
}
Another issue: printReversedWord is missing the subscript operator on word:
void printReversedWord(char *word) {
for (size_t i = strlen(word); i-- > 0;) {
printf("%c", word[i]); // <- here
}
}
Demo
The function strcat deals with strings. That means that the both function arguments shall point to strings. And the function also builds a string.
Also as your function does not change passed strings then its parameters should have qualifier const. And the function shall build a new string. Otherwise after this statement
w1 = (char*)malloc(sizeof(char) * len);
the first passed string is lost.
The function can look the following way
char * concatWords( const char *w1, const char *w2, const char *w3 )
{
size_t len = strlen( w1 ) + strlen( w2 ) + strlen( w3 );
char *result = malloc( len + 1 );
if ( result != NULL )
{
strcpy( result, w1 );
strcat( result, w2 );
strcat( result, w3 );
}
return result;
}
In turn the function printReversedWord can look the following way/ Pay attention to that your function implementation has a bug in this statement
printf("%c", word);
You have to write
printf("%c", word[i]);
Here is the updated function definition.
void printReversedWord( const char *word )
{
size_t len = strlen( word );
while ( len != 0 )
{
putchar( word[--len] );
}
putchar( '\n' );
}
In main you should write
initWords( word1, word2, word3 );
longWord = concatWords(word1, word2, word3);
if ( longWord != NULL ) printReversedWord(longWord);
free( longWord );
char* concatWords(char *w1, char *w2, char *w3) {
int len = strlen(w1) + strlen(w2) + strlen(w3);
w1 = (char*)malloc(sizeof(char) * len);
strcat(w1, w2);
strcat(w1, w3);
return w1;
}
This method:
allocates memory that is not zeroed out, which will make it impossible to "cat" to it, because that assumes the string is null-terminated.
allocates memory and then overwrites the pointer that points to the first word. That would be lost.
allocates only memory for the three words, but misses the final zero that needs to be written to a null-terminated string
does not communicate clearly, which parameters will or will not be changed be the function
So to fix this:
char* concatWords(const char* w1, const char* w2, const char* w3) {
int len = strlen(w1) + strlen(w2) + strlen(w3);
char* all = calloc(len + 1, sizeof(char));
strcat(all, w1);
strcat(all, w2);
strcat(all, w3);
return all;
}
char* concatWords(char *w1, char *w2, char *w3) {
int len = strlen(w1) + strlen(w2) + strlen(w3);
w1 = (char*)malloc(sizeof(char) * len);
strcat(w1, w2);
strcat(w1, w3);
return w1;
}
This function needs a fair bit of work. I'm assuming you want to allocate a new string sufficiently large for the job, concatenate w1, w2 and w3 into it, and return that new string.
Firstly, you overwrote the pointer for w1 in this line:
w1 = (char*)malloc(sizeof(char) * len);
So (in that function) you've now lost whatever was pointed to by w1, because w1 is now pointed to newly-allocated memory.
Secondly, you've not allocated enough memory to store the string: you need an extra +1 to include the terminal NUL character.
The original values of w1, w2 and w3 passed into the function were cast to (char *) from the base addresses of char[] arrays, so you can't use w1 = realloc(w1, len+1); - you'll need a new variable to store the new pointer.
Your concatWords() function should look like this:
char* concatWords(const char * const w1,
const char * const w2,
const char * const w3) {
int len = strlen(w1) + strlen(w2) + strlen(w3) + 1; // Note the +1!
new_w = (char*)malloc(sizeof(char) * len);
strcpy(new_w, w1);
strcat(new_w, w2);
strcat(new_w, w3);
return new_w;
}
Note that I've changed the function's parameter types, so that you don't accidentally change the values of the w1, w2 or w3 pointers, or the memory they point to. Always use minimum viable permissions, to prevent the risk of memory corruption. In C terms, if you can const it, const it!
Your printReversedWord(char *word) function won't work either. You're passing word to printf() as the argument to its "%c" format string, not the contents of the memory it's pointing to. You never use the index variable i!
This will have the effect of passing either the first or the last byte of the pointer value as the character to print, depending whether your CPU's big-endian or little-endian.
So if word's value as a pointer was 0x12345678 (the location of the first character in the string it points to), you'd either be printing 0x12 (unprintable control character DC2) or 0x78 ('x') over and over again instead of the reversed contents of word. Of course, the actual effect would depend on the real pointer's value, not my example here.
It should be (something like):
void printReversedWord(const char * const word) {
int len = strlen(word);
for (int i = len - 1; i >=0; i--) {
printf("%c", word[i]);
}
printf("\n"); // Don't forget the newline!
}
Note the [i] in the printf() argument. It indexes into the char array pointed to by word. Note also (again) the improvement in the parameter type. I've also added a newline at the end.
Finally, looking at:
#define N 10
void initWords(char *w1, char *w2, char *w3) {
printf("Enter first word:\n");
scanf("%10s", w1);
printf("Enter second word:\n");
scanf("%10s", w2);
printf("Enter third word:\n");
scanf("%10s", w3);
}
You've defined N, but then explicitly embedded 10 in the scanf() format strings. What happens if you change the value of N at a later point?
I'll leave the solution for that as an exercise for the student. :)
The max length of each string is defined in a globally available token... Why not use it (and temporarily use a bit too much dynamic memory)?
char* concatWords(char *w1, char *w2, char *w3) {
char *p = (char*)malloc( (3*N+1) * sizeof *p );
if( p )
sprintf( p, "%s%s%s", w1, w2, w3 );
return p;
}
One could also add checking that all 3 incoming pointers are not NULL...
#include <stdio.h>
#include <stdlib.h>
char concaten(const char *str1,const char *str2);
int main()
{
printf("%s",concaten("Code","blocks"));
return 0;
}
char concaten(const char *str1,const char *str2) {
int i=0,j=0;
char *result;
while(*str1){
result[i++]=str1[i++];
}
while(*str2){
result[i+j++]=str2[j++];
}
return result;
}
I wrote this function to get two strings and add them to another third string; I don't understand where I am going wrong, as it doesn't print anything.
There are a number of problems with your concaten function.
First, it should be returning a char* pointer, not a char; thus, the declaration should look like this:
char* concaten(const char* str1, const char* str2);
Next, the function will need to allocate memory in which to store the concatenated strings; this can be done with the malloc() function, and the number of characters required will be the sum of the lengths of the two input strings plus one, for the required nul-terminator.
Third, the logic of your two loops is wrong. You are incrementing i and j twice per loop but not incrementing either of the source pointers.
Finally, you must add a nul-terminator at the end of your new string.
Here's a version with the above fixes applied:
char* concaten(const char* str1, const char* str2)
{
int i = 0, j = 0;
char* result = malloc(strlen(str1) + strlen(str2) + 1); // allow space for nul-terminator
while (*str1) {
result[i++] = *str1++; // Only increment i once and "str1" once
}
while (*str2) {
result[i + j++] = *str2++; // Only increment j once and "str2" once
}
result[i + j] = '\0'; // Add required nul-terminator
return result;
}
Also, as you have allocated memory (with the malloc call), you should release that when you're done with the data, using a call to free. Here's how your main might work:
int main(void)
{
char* answer = concaten("Code", "blocks");
printf("%s", answer);
free(answer);
return 0;
}
Note: You can also remove the j variable entirely, and just re-use the result[i++] expression in the second loop. I've left it in so that you can more easily relate my code to your own.
Your function has the return type char
char concaten(const char *str1,const char *str2);
but within the function you are returning the variable result
return result;
declared like a pointer of the type char *
char *result;
So the compiler will issue a message that you are trying to convert a pointer to an integer.
The function must be declared like
char * concaten(const char *str1,const char *str2);
The pointer result is not initialized and has an indeterminate value. You need to allocate memory where you will write concatenated strings.
The while loops in the function will be infinite if str1 and/or str2 are not empty strings due to conditions
while(*str1){
and
while(*str2){
These statements
result[i++]=str1[i++];
and
result[i+j++]=str2[j++];
invoke undefined behavior not only because the pointer result is not initialized but also because there is no sequence point between left and write operands where there is used the postfix increment operator ++.
Also the result string must be zero terminated.
If you are not allowed to use standard C string functions then your function can be implemented for example the following way
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = 0;
size_t n2 = 0;
while ( str1[n1] ) ++n1;
while ( str2[n2] ) ++n2;
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
char *p = result;
while ( *str1 ) *p++ = *str1++;
do
{
*p++ = *str2;
} while ( *str2++ );
}
return result;
}
Also you should not forget to free the allocated memory when the result string is not needed any more.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = 0;
size_t n2 = 0;
while ( str1[n1] ) ++n1;
while ( str2[n2] ) ++n2;
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
char *p = result;
while ( *str1 ) *p++ = *str1++;
do
{
*p++ = *str2;
} while ( *str2++ );
}
return result;
}
int main(void)
{
char *result = concatenate( "Code ", "blocks" );
if ( result != NULL ) puts( result );
free( result );
return 0;
}
The program output is
Code blocks
If you may use standard C string functions then the function concatenate can look as it is shown in the demonstrative program below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = strlen( str1 );
size_t n2 = strlen( str2 );
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
memcpy( result, str1, n1 );
memcpy( result + n1, str2, n2 + 1 );
}
return result;
}
int main(void)
{
char *result = concatenate( "Code ", "blocks" );
if ( result != NULL ) puts( result );
free( result );
return 0;
}
The program output is the same as shown above that is
Code blocks
Aside from the fact that your function should not return char but char*, the expression result[i++] = str1[i++]; is not correct it lacks a sequence point. Furthermore result is an unitialized pointer, it cannot hold any data, you would need to make it point to some valid memory location.
You could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* concatenate(const char* str1, const char* str2 ){
char* result = malloc(strlen(str1) + strlen(str2) + 1);
if (result){
char* temp = result;
while (*str1 != '\0'){
*temp++ = *str1++;
}
while (*str2 != '\0'){
*temp++ = *str2++;
}
*temp = '\0'; // don't forget to null terminate the string
}
return result; // if memory allocation fails a null pointer is returned
}
The direct usage of the function in the printf statement will not allow you to free the memory and you would have a memory leak if the program didn't finish immediately, in these cases it's best to have the returned pointer assigned as to not lose track of the allocated memory:
int main(void){
char *result = concatenate("Code", "blocks");
if(result){
printf("%s", result);
free(result);
}
return EXIT_SUCCESS;
}
I am executing following C program and getting runtime error as "free(): Invalid Pointer"
#include<stdio.h>
#include<stdlib.h>
static void freeArgs(char** args);
int main()
{
char** argv = calloc(4, 10);
int argc = 0;
argv[argc++]="yogita";
argv[argc++] ="negi";
argv[argc] = NULL;
freeArgs(argv);
return 0;
}
static void freeArgs(char** args)
{
char** af = args;
for (; *af; af++)
free(*af);
free(args);
}
Can anyone suggest me the solution?
free(*af);
tries to free memory that was not allocated through malloc/calloc:
argv[argc++]="yogita";
"yogita" is a string literal, thus not dynamically allocated. You can't free its memory.
This call
char** argv = calloc(4, 10);
(Note:it seems you mean
char** argv = calloc( 4, sizeof( char * ) );
end note)
with the magic number 10 allocates dynamically only one extent of memory of the size 4 * 10 that is equal to 40. So you need to call the function free only one time for the allocated extent. That is how many times malloc or calloc was called the same number of times free should be called.
I suspect that you meant something like the following
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void freeArgs( char **args )
{
char **af = args;
while ( *af ) free( *af++ );
free( args );
}
int main(void)
{
enum { N = 4, LEN = 10 };
char **argv = calloc( N, sizeof( char * ) );
size_t argc = 0;
argv[argc] = malloc( LEN * sizeof( char ) );
strcpy( argv[argc++], "yogita" );
argv[argc] = malloc( LEN * sizeof( char ) );
strcpy( argv[argc++], "negi" );
freeArgs( argv );
return 0;
}
I am trying to make a function that receives a dynamic string and removes from it all occurrences of the character also passed as a parameter.
The string should finally contain just enough space to contain characters not deleted
void delete(char *cad, char c){
int i, cont = 0;
char *aux = NULL;
i = 0;
while(cad[i] != '\0'){
if(cad[i] != c){
aux = (char*)realloc(aux, sizeof(char) * cont + 1);
aux[cont] = cad[i];
cont++;
}
i++;
}
cad = (char*)realloc(cad, sizeof(char) * cont);
i = 0;
while(aux[i] != '\0'){
cad[i] = aux[i];
i++;
}
}
Now I have a segmentation fault
You do not check the result of the realloc.
IMO it will be better to return the pointer to the new string instead of using double pointer. Double pointer may cause hard to track memory leaks, and function will not work with the const strings - for example string literals
You do not null character terminate the string.
In this example, I did not change your allocation algorithm but in real life more efficient will be first to count how much memory you need to allocate, allocate it and then process the string again:
char *delete(const char *cad, char c){
size_t nchars = 0;
char *aux = NULL;
char *temp;
while(*cad)
{
if(*cad != c)
{
temp = realloc(aux, sizeof(*temp) * nchars + 1);
if(temp)
{
aux = temp;
aux[nchars++] = *cad;
}
else
{
/* handle allocation error */
free(aux);
aux = NULL;
break;
}
}
cad++;
}
if(aux) aux[nchars] = 0;
return aux;
}
Some minor changes: use objects instead of types in sizeof and do not cast result of malloc. You can also add NULL pointer parameter check.
Every time you are reallocing inside the while loop, you are essentially giving the variable aux a new address each time.
I advise you to not do that and allocate the memory you want to allocate at the start of the function.
You will need to calculate how much memory you would need before allocating the memory. That is, count how much element you would delete.
If you want me to further elucidate or add a code fragment, please feel free to ask it in the comments.
Instead of many calls to realloc() I would just perform an in-place substitution of the characters; this substitution leaves unused allocated characters at the end of the string and is illustrated by the delete_no_realloc() function below.
If you want to get rid of these unused ending characters in the allocated string, then only one call to realloc() is needed as illustrated by the delete() function below.
Note that when a function uses realloc() on a parameter which is a pointer, it must obtain the address of this pointer to adjust it with the result of realloc().
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t // new length
delete_no_realloc(char *cad,
char c)
{
size_t w=0;
for(size_t r=0; cad[r]; ++r)
{
char ch=cad[r];
if(ch!=c)
{
cad[w++]=ch; // store and advance write index
}
}
cad[w]='\0'; // ensure string termination
return w;
}
void
delete(char **cad_ptr,
char c)
{
char *cad=*cad_ptr; // forget this embarrassing indirection
size_t new_length=delete_no_realloc(cad, c);
cad=realloc(cad, new_length+1);
if(cad==NULL)
{
abort();
}
*cad_ptr=cad; // don't forget to adjust the string
}
int
main(void)
{
const char *msg="this is a message";
char *cad=malloc(strlen(msg)+1);
if(cad==NULL)
{
abort();
}
strcpy(cad, msg);
printf("before: <%s>\n", cad);
delete(&cad, 's'); // pass the address of the string
printf("after: <%s>\n", cad);
free(cad);
return 0;
}
You can simplify your delete() function by simply using a read and write index within the original string, removing all c characters found, and then make a single call to realloc() to reallocate storage to exactly fit the remaining characters.
You can do something like:
void delete (char **cad, char c)
{
if (!*cad || !**cad) /* check if cad is NULL or empty-string */
return;
size_t write = 0; /* write index */
for (size_t read = 0; (*cad)[read]; read++) { /* loop over each char in cad */
if ((*cad)[read] != c) /* if char not c */
(*cad)[write++] = (*cad)[read]; /* copy incrementing write */
}
(*cad)[write] = 0; /* nul-terminate */
void *tmp = realloc (*cad, write + 1); /* realloc to exact size */
if (!tmp) { /* validate realloc */
perror ("realloc-cad");
return;
}
*cad = tmp; /* assign reallocated block to *cad */
}
A full example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void delete (char **cad, char c)
{
if (!*cad || !**cad) /* check if cad is NULL or empty-string */
return;
size_t write = 0; /* write index */
for (size_t read = 0; (*cad)[read]; read++) { /* loop over each char in cad */
if ((*cad)[read] != c) /* if char not c */
(*cad)[write++] = (*cad)[read]; /* copy incrementing write */
}
(*cad)[write] = 0; /* nul-terminate */
void *tmp = realloc (*cad, write + 1); /* realloc to exact size */
if (!tmp) { /* validate realloc */
perror ("realloc-cad");
return;
}
*cad = tmp; /* assign reallocated block to *cad */
}
int main (int argc, char **argv) {
if (argc < 3) {
fputs ("usage: ./prog \"string with c\" c\n", stderr);
return 1;
}
size_t len = strlen (argv[1]);
char *s = malloc (len + 1);
if (!s) {
perror ("malloc-s");
return 1;
}
memcpy (s, argv[1], len + 1);
printf ("%s (%zu chars)\n", s, len);
delete (&s, *argv[2]);
printf ("%s (%zu chars)\n", s, strlen(s));
free (s);
}
Example Use/Output
$ ./bin/delete_c_realloc "nmyn ndogn nhasnn nnfleasnnn" n
nmyn ndogn nhasnn nnfleasnnn (28 chars)
my dog has fleas (16 chars)
Look things over and let me know if you have questions.
There are four main problems with your function implementation.
The first one is that the function accepts the pointer to the source string by value. That is the parameter cad is initialized by the value of the pointer used as an argument. As a result changing the variable cad does not influence on the original pointer.
The second one is that you are not checking whether a call of realloc was successful. As a result the function can invoke undefined behavior.
The third one is that it is inefficient to reallocate the string each time when a new character is appended.
And at last the fourth one is that the result dynamically allocated array does not contain a string because you forgot to append the terminating zero character '\0'.
If you want to change within the function a value of the original pointer you should either to return from the function the result pointer obtained in the function and assign it to the original pointer in the caller. Or you should pass the original pointer to the function by reference. In C passing by reference means passing an object (that can be a pointer) indirectly through a pointer to it.
Here is a demonstrative program that shows the function implementation when the original pointer is accepted by the function by reference.
The function also returns a pointer to the result string that can be checked in the caller whether the reallocation of dynamic memory within the function was successful.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * remove_char( char **s, char c )
{
char * result = *s;
if ( c != '\0' )
{
char *dsn = *s;
const char *src = *s;
do
{
if ( *src != c )
{
if ( dsn != src )
{
*dsn = *src;
}
++dsn;
}
} while ( *src++ );
char *tmp = realloc( *s, ( dsn - *s ) * sizeof( char ) );
if( tmp != NULL ) *s = tmp;
result = tmp;
}
return result;
}
int main(void)
{
char *s = malloc( 12 );
strcpy( s, "H#e#l#l#o!" );
puts( s );
if ( remove_char( &s, '#' ) ) puts( s );
free( s );
return 0;
}
The program output is
H#e#l#l#o!
Hello!
Another approach is to write a function that does not change the source string but creates dynamically a new string that contains the source string excluding the specified character. Such a function is more flexible because you can call it with string literals. If the source string also was dynamically allocated then the caller of the function after a successful call it can just free the source string.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * remove_copy( const char *s, char c )
{
size_t src_len = strlen( s );
size_t dsn_len = src_len;
if ( c != '\0' )
{
for ( const char *p = s; ( p = strchr( p, c ) ) != NULL; ++p )
{
--dsn_len;
}
}
char *result = malloc( ( dsn_len + 1 ) * sizeof( char ) );
if ( result != NULL )
{
const char *src_s = s;
char *dsn_s = result;
if ( dsn_len != src_len )
{
for ( const char *p = src_s;
( p = strchr( src_s, c ) ) != NULL;
src_s = p + 1 )
{
if ( p - src_s != 0 )
{
memcpy( dsn_s, src_s, p - src_s );
dsn_s += p - src_s;
}
}
}
strcpy( dsn_s, src_s );
}
return result;
}
int main(void)
{
char s[] = "H#e#l#l#o!";
puts( s );
char *p = remove_copy( s, '#' );
if ( p != NULL ) puts( p );
free( p );
return 0;
}
The program output is the same as shown for the preceding demonstrative program that is
H#e#l#l#o!
Hello!
I'm trying to write a function that removes whitesapces from a string and convert it to lower case.
My code below doesn't return anything. Why?
char *removeSpace(char *st);
int main(void){
char *x = "HeLLO WOrld ";
x = removeSpace(x);
printf("output: %s\n", x);
}
char *removeSpace(char *st)
{
int c = 0;
char *s = malloc(sizeof(strlen(st)+1));
for (int x = 0; x < strlen(st); x++)
{
if (st[x] != ' ')
{
s[x] = tolower(st[x]);
}
}
st= s;
st= s;
return st;
}
The malloc statement uses sizeof unnecessarily as mentioned in the comments. You also have an error in the assignment of characters to the new string:
s[x] = tolower(st[x]);
You use the same index to the new string s as the old string st. This isn't right as soon as you remove any spaces. So for example indexes 0 through 4 line up between the two strings as you copy hello but then you skip a space at index 5 and then you want to assign the w at st[6] to s[5]. This means you need a separate index to track where you are in the destination string. So you need something like this code, which cleans up malloc(), adds the missing header includes, and introduces a new index for the output string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *removeSpace(char *st);
int main(void){
char *x = "HeLLO WOrld ";
x = removeSpace(x);
printf("output: %s\n", x);
}
char *removeSpace(char *st)
{
size_t len = strlen(st);
int newStrIdx = 0;
char *s = malloc(len+1);
for (int x = 0; x < len; x++)
{
if (st[x] != ' ')
{
s[newStrIdx++] = tolower(st[x]);
}
}
s[newStrIdx] = '\0';
return s;
}
Oh, and you forgot the null-terminate the output string, which I added at the end.
char *s = malloc(sizeof(strlen(st)+1));
you have a couple of nested expressions, and you jumped exactly the wrong way in the comment thread (I guess it was 50:50).
strlen(st) is the number of characters in the string st
strlen(st)+1 is the correct number of characters to allocate for a copy
... looking good so far!
sizeof(strlen(st)+1) is the size in bytes required to represent the type of that value. So if size_t is an 4-byte unsigned int, this sizeof expression is just 4.
The value of the string length is thrown away at this point.
Now, you want to allocate enough bytes for the string, not enough bytes to save the string's length as a size_t value. Just remove the sizeof entirely.
Oh, and also - st = s doesn't do anything here. The variable st is local inside the function, and doesn't affect anything outside. Returning s is sufficient.
For starters if you want to create a copy of a string then the function declaration shall look like
char * removeSpace( const char *st);
that is the original string is not changed within the function.
And as you are passing to the function a string literal
char *x = "HeLLO WOrld ";
x = removeSpace(x);
then indeed it may not be changed within the function. Any attempt to change a string literal results in undefined behavior.
The expression used in the call of malloc
sizeof(strlen(st)+1)
is equivalent to the expression
sizeof( size_t )
due to the fact that the function strlen has the return type size_t.
So this expression does not yield the length of the source string.
Moreover there is no need to allocate a string with the size equal to the size of the source string because the destination string can have much less characters (due to removing spaces) than the source string.
The assignment in the if statement
if (st[x] != ' ')
{
s[x] = tolower(st[x]);
}
uses an invalid index in the expression s[x]. That is as a result the destination string will contain gaps with uninitialized characters.
Also the terminating zero character '\0' is not appended to the destination string
Take into account that the set of white space characters includes other characters as for example the tab character '\t' apart from the space character ' '.
The function can be defined the following way.
char * removeSpace( const char *st )
{
size_t n = 0;
for ( const char *src = st; *src; ++src )
{
if ( !isspace( ( unsigned char )*src ) ) ++src;
}
char *result = malloc( n + 1 );
result[n] = '\0';
for ( char *dsn = result; *st; ++st )
{
if ( !isspace( ( unsigned char )*st ) )
{
*dsn++ = tolower( ( unsigned char )*st );
}
}
return result;
}
And the function can be called like
char *st = "HeLLO WOrld ";
char *dsn = removeSpace( st );
puts( dsn );
free( dsn );
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char * removeSpace( const char *st )
{
size_t n = 0;
for ( const char *src = st; *src; ++src )
{
if ( !isspace( ( unsigned char )*src ) ) ++src;
}
char *result = malloc( n + 1 );
result[n] = '\0';
for ( char *dsn = result; *st; ++st )
{
if ( !isspace( ( unsigned char )*st ) )
{
*dsn++ = tolower( ( unsigned char )*st );
}
}
return result;
}
int main(void)
{
char *st = "HeLLO WOrld ";
char *dsn = removeSpace( st );
puts( dsn );
free( dsn );
return 0;
}
Its output is
helloworld