3

I am trying to implement my version of strcat. However, I get below warning and my code crashes while running. I am passing &p from main to make permanent changes to variable p in the main function.

Warning:

note: expected ‘char **’ but argument is of type ‘char (*)[10]’

Code:

#include <string.h>
#include <stdio.h>

void mystrcat(char **p, char *q)
{
    while (**p != '\0')
    {
        *p++;
    }

    while (*q != '\0')
    {
        **p = *q;
        (*p)++;
        q++;
    }
    *p = '\0';
}

int main()
{
    char p[10] = "ravi";
    char q[12] = "ra";

    mystrcat(&p, q);
    printf("%s", p);
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Ravi
  • 173
  • 1
  • 10
  • try `char *p="ravi";` – tstanisl Jul 08 '21 at 08:32
  • 1
    @tstanisl that would be UB. – alex01011 Jul 08 '21 at 08:37
  • No, I can not change char p[10]="ravi"; that is not what I wanted as I want my program to run in any scenario even if array of char is given instead of pointer to char – Ravi Jul 08 '21 at 08:44
  • Perhaps a good first step would be looking at the interface of the standard `strcat`. Does it have a `char **` somewhere? Why do you think you need one? – n. m. could be an AI Jul 08 '21 at 08:49
  • Are you planning to use this with dynamically allocated arrays too? Is that the reason why the first argument is a `char**`, to be able to modify that pointer? If that is the case, you should also pass the allocated size, otherwise there's no need to pass a pointer to a pointer. – Bob__ Jul 08 '21 at 09:10
  • @alex01011, your're right, I haven't noticed that memory is modified via `p`. – tstanisl Jul 08 '21 at 09:18
  • there are lots of mistakes in this code. It would be a lot simpler to make the function have parameters `(char *p, char const *q)` , then this issue wouldn't even arise – M.M Jul 08 '21 at 09:51

3 Answers3

6

The variable p is declared as having the type char[10]

char p[10]="ravi";

So the expression &p in this call

mystrcat(&p, q);

has the type char ( * )[10].

However the corresponding function parameter has the type char **.

void mystrcat(char **p, char *q)

and there is no implicit conversion from the type char ( * )[10] to the type char **. So the compiler issues an error.

You could call your function the following way

char *tmp = p;
mystrcat( &tmp, q );

But in any case the function is incorrect at least due to this while loop

while(**p != '\0')
{
    *p++;
}

because the expression

*p++

is equivalent to the expression

*( p++ )

while you need the expression

( *p )++

or

++*p

Or the last statement shall be

**p = '\0';

instead of

*p = '\0';

There is no any sense to declare the first function parameter as having the type char ** instead of the type char * as it is declared in the standard C function strcat.

char * strcat(char * restrict s1, const char * restrict s2);

Moreover the second parameter shall have the qualifier const because the corresponding string is not changed within the function. And the function should return a pointer to the destination string.

So the function can look the following way

char * mystrcat( char *p, const char *q )
{
    char *s = p;

    while ( *s ) ++s;

    while ( ( *s++ = *q ) != '\0' ) ++q;

    return p;
}

and be called like

puts( mystrcat( p, q ) );

Here is a demonstrative program.

#include <stdio.h>

char * mystrcat( char *p, const char *q )
{
    char *s = p;

    while ( *s ) ++s;

    while ( ( *s++ = *q ) != '\0' ) ++q;

    return p;
}

int main(void) 
{
    char p[10] = "ravi";
    char q[12] = "ra";
    
    puts( mystrcat( p, q ) );
    
    return 0;
}

The program output is

ravira
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

Instead of a pointer to a pointer to char, just pass a pointer to char, it will also make the modification permanent. That's pretty much how the string.h strcat works. Maybe also check for NULL pointers.

#include <stdio.h>
#include <stdlib.h>

char *mystrcat(char *p, char *q)
{
    char *ptr = p; /* so you dont alter p's initial address */

    if (p == NULL || q == NULL)
    {
        exit(EXIT_FAILURE);
    }

    while(*ptr != '\0')
    {
        ptr++;
    }

    while(*q != '\0')
    {
        *ptr = *q;
        ptr++; /* you dont have to dereference */
        q++;
    }
    *ptr = '\0';

    return p;
}

int main(void)
{
    char p[100]="hello";
    char q[12]="world";

    mystrcat(p, q);
    printf("%s", p);

    return 0;
}
alex01011
  • 1,670
  • 2
  • 5
  • 17
  • I am surprised why I do not need pointer to a pointer to char to make changes permanent. Think of a scenario where you want to swap two variables in a function permanently, you have to pass its address, then why not in this case? – Ravi Jul 08 '21 at 09:33
  • `char *ptr = p; /* so you dont alter p's initial address , in case of a dynamic string */` does not make sense, `p` in `mystrcat` is already a copy of `p` in `main` (decayed to a pointer). No need to make an additional copy. – mch Jul 08 '21 at 09:45
  • @Ravi `char *p` literally is passing the address of the characters in the string – M.M Jul 08 '21 at 09:54
  • @mch yes you are right indeed, I forgot to change the return type, to `char *`. Edited. – alex01011 Jul 08 '21 at 10:57
  • @alex01011 I still see no reason for this line. Just remove it and use `p` instead of `ptr` does exactly the same thing. – mch Jul 08 '21 at 12:20
  • @mch, consider the following, https://godbolt.org/z/896r7cqcc. Now p, points to the end of string, so a function like `printf()` or `puts()` will only see `\0`. – alex01011 Jul 08 '21 at 12:28
  • @alex01011 And what's the difference to your version? https://godbolt.org/z/cbfsaEcxv If you print the return value of it, it does not matter if the function works on a copy of the pointer like I suggest or on a copy of the copy of the pointer, like you did, `printf("%s", p);` works in both cases and your version creates a copy of the copy of the pointer and tell the reader in the comment something that's not true. – mch Jul 08 '21 at 16:12
  • @mch Yeah I'm sorry for messing the `return`. You are making this a big deal anyways, this how the `strcat` from `string.h` works. Appreciate the dvote btw. – alex01011 Jul 08 '21 at 16:18
0

You will need to pass a pointer that points to each individual character of the array:

int main()
{
 char p[10]="ravi5";
 char q[12]="ra54";
 
 char* pPointer = p;
 char** pPointerPointer = &pPointer;

 mystrcat(pPointerPointer, q);
 printf("%s", p);
 return 0;
}

Note: In your strcat function you missed the parenthesis:

while(**p != '\0')
{
 (*p)++;
}
iduque
  • 1