0

I'm trying to write a program that catenate 2 values in C without using strcat().

char cat (char s1, char s2){
    char s3[200];
    strcpy(s3,s1);
    strcpy(s3+strlen(s1),s2);
    return s3;
}

This is my code but it's giving this error:

argument of type "char" is incompatible with parameter of type "const char*"

What should l do? (I recently start to learn C so please answer me in an easy way)

J...S
  • 5,079
  • 1
  • 20
  • 35
S.keshavarz
  • 21
  • 1
  • 2
  • 3
    If you're learning C, do not tag your questions with C++ — it is a different language and answers that are correct for C++ are usually not helpful in C, and answers for C are typically not a good choice for C++. – Jonathan Leffler May 03 '18 at 04:27
  • Please excuse this sounding cynical, but dealing with pointers and "string" manipulation should be later in your path to learn C. Have a look here https://ericlippert.com/2014/03/21/find-a-simpler-problem/ – Yunnosch May 03 '18 at 04:43
  • This is one of the questions which together make a duplicate https://stackoverflow.com/questions/890535/what-is-the-difference-between-char-const-and-const-char – Yunnosch May 03 '18 at 04:45
  • The other duplicate should be about the difference of char and char*, I can't find a good one now.... – Yunnosch May 03 '18 at 04:50
  • To solve your immediate problem: You seem to be calling your function with at leat one parameter being a string literal, e.g. `cat("parameter", "string");`. Those parameters are practically pointers to constant strings, i.e. `cosnt char*`. – Yunnosch May 03 '18 at 04:54
  • Isn't this Undefined Behavior because the `s3` is local to `cat` function and when you return it, you return it's address which might not work once the function frame is gone from the stack ? – Shubham May 03 '18 at 05:02
  • 3
    You should take a C book and read about pointers ands strings in C – 0___________ May 03 '18 at 05:54
  • 1st question to ask: "*catenate 2 values*" exactly which kind of values? – alk May 03 '18 at 06:46
  • @JonathanLeffler Thanks. I'll pay attention next time. – S.keshavarz May 04 '18 at 07:10
  • @Yunnosch Thanks for all your helps.But I wanted to write it without pointers. – S.keshavarz May 04 '18 at 07:13
  • @alk char arrays – S.keshavarz May 04 '18 at 07:13

3 Answers3

1

In your function, the parameters s1 and s2 are of type char which means a single character. Inorder for them to be strings, they must be character arrays. So

char cat (char s1[], char s2[]){

or

char cat (char *s1, char *s2){

instead of

char cat (char s1, char s2){

After this correction, you could just use sprintf() if the destination string is large enough like

sprintf(s3, "%s%s", s1, s2);

And in your program,

s3 is allocated on the stack as it is an automatic variable.

It goes out of scope when the program control exits the cat() function. If you really need to return the string, either allocate memory for s3 on the heap using malloc() and return a pointer to that memory as in

char* cat (char s1[], char s2[]){
    char *s3 = NULL;
    if( (s3=malloc(sizeof(char)*( strlen(s1)+strlen(s2)+1 )))==NULL )
    {
            perror("Not enough memory");  
            return NULL;
    }
    sprintf(s3, "%s%s", s1, s2);
    return s3;
}

or create the s3 character array in the calling function and pass it to cat() as in

char s3[200];
cat(s3, s1, s2);

........

void cat (char s3[], char s1[], char s2[]){
    if( strlen(s1) + strlen(s2) < 200 )//where 200 is the size of s3
    {
        sprintf(s3, "%s%s", s1, s2);
    }
    else
    {
        printf("\nInput strings too large");
    }
}

See Returning C string from a function.

J...S
  • 5,079
  • 1
  • 20
  • 35
  • I don't mind the downvote much but please tell me where I am wrong. – J...S May 03 '18 at 05:32
  • Upvoted as the key issues are addressed correctly. But your solutions are unnecessarily complicated; I *guess* that's the reason for the downvote.. –  May 03 '18 at 06:02
  • @FelixPalmen Which part is complicated? Any idea how to simplify it? – J...S May 03 '18 at 06:48
  • WHy did you chose to limit the string length to 200? Not my DV BTW. – Jabberwocky May 03 '18 at 06:56
  • @MichaelWalz The OP chose 200 as size in the question. Could I've made it better? And DV? What's that (Google didn't make sense to me)? – J...S May 03 '18 at 06:59
  • @J...S I'd say for example using `sizeof(char)` (which is 1 by definition), and, of course, the use of `sprintf()`, which works, but is really unnecessary here. See my answer for what I'd consider simple code. (btw, DV is short for downvote) –  May 03 '18 at 07:02
  • @J...S yes, it could have been better by using `strlen(s1) + strlen(s2) + 1`. Not my DV though. – Ajay Brahmakshatriya May 03 '18 at 07:04
  • Btw, now looking closely at your code, there's an error. Although you **do** check for `NULL` returned by `malloc()`, you nevertheless continue using that null pointer... –  May 03 '18 at 07:08
  • @FelixPalmen Well spotted. I made a modification. – J...S May 03 '18 at 07:22
  • @AjayBrahmakshatriya Do you mean the size of `s3`? If so that would mean a VLA. I've seen people saying that VLA support is optional in later C standards. Doesn't that mean use of VLA is to be discouraged? – J...S May 03 '18 at 07:29
  • 1
    I meant instead of `200` in `s3=malloc(sizeof(char)*200))`. The choice of size (`200`) seems arbitrary. – Ajay Brahmakshatriya May 03 '18 at 07:55
  • 1
    @J...S if you use `malloc()`, this has nothing to do with VLAs. In the case without `malloc()`, the function doesn't care how the memory passed to it is allocated. But regarding VLAs, they are indeed optional in C11, so if you aim for full portability, don't use them. On the other hand, they are still well-defined (just optional) and in practice, any major compiler supporting C11 also supports C99, so must implement them anyways -- therefore it's unlikely to come across a C11 compliant compiler that doesn't support them. –  May 03 '18 at 08:14
  • @AjayBrahmakshatriya You're right, edited. – J...S May 03 '18 at 08:16
  • @J...S then remove the check for `200` as well, it doesn't make much sense to allocate the needed memory, but return an uninitialized chunk of memory if it's larger than 200 .... –  May 03 '18 at 08:17
  • 1
    @J...S anyway, VLAs aren't a correct solution for this problem because they are out of scope when the function ends. The correct solution if either to use `malloc` (or family of functions) or take appropriately allocated `s3` from user. – Ajay Brahmakshatriya May 03 '18 at 08:19
  • @FelixPalmen Missed that. Done. – J...S May 03 '18 at 08:21
0

You're taking chars as arguments and using it as a return value. You probably want s1, s2 to be const char *:

char *cat (const char *s1, const char *s2){

You will have to return a char * too, which means you will have to allocate something that isn't on the stack.

char *cat (const char *s1, const char *s2){
    char *s3 = (char*)malloc(200);
    strcpy(s3,s1);
    strcpy(s3+strlen(s1),s2); // strcat(s3, s1);
   return s3;
}
N00byEdge
  • 1,106
  • 7
  • 18
0

A char is a small integer type (a byte), which can hold just a single encoded character, not a string. Strings in C don't have a dedicated type, they're just defined as a sequence of characters, ending with a 0 byte. So, you naturally store strings in arrays of char. E.g. the array

char str[30];

Can store a string of up to 29 characters (one more is needed for the terminating 0).

Furthermore, you have to know that arrays can't be passed to or returned from functions in C. Instead, pointers are used. If you write arrays as function arguments or return values, these types are automatically adjusted to the corresponding pointer types. It's common to say the array "decays" as a pointer. So, in your code, you attempt to return an array. Instead, a pointer to the first array element is returned. Because this array is local to your function, it doesn't exist any more after the return, so you are returning an invalid pointer.

That's why the library function strcat expects the caller to give a pointer to the result, instead of returning the result. A typical simple strcat function could look like this (not the original, returning nothing here to make the code simple):

void mystrcat(char *s, const char *append)
{
    while (*s) ++s; // search end of s
    while ( (*s++ = *append++) ); // copy until end of append
}

To understand this code, you have to know that 0 is false in C when evaluated in a boolean context, and any other value is true. ++ increments, so applied to pointers, moves them to point to the next element. Therefore, this code examines each character in *s until it finds a 0 byte and then starts copying characters from *append there until the 0 byte in *append is hit.


If you absolutely want to return the result, you have to dynamically allocate memory for it in your function. This could look like the following:

char *concatenate(const char *s1, const char *s2)
{
    size_t resultlen = strlen(s1) + strlen(s2) + 1; // one more for the 0 byte
    char *result = malloc(resultlen);
    if (!result) return 0; // allocation failed, out of memory

    char *p = result;
    while ( (*p = *s1++) ) ++p; // copy from s1 until 0
    while ( (*p++ = *s2++) );   // copy from s2 until 0

    return result;
}

Of course, in this case, the caller has to free() the result when it's no longer needed.

  • Rather, a typical strcat would probably look like `strcpy(&s[strlen(s)], append);`. This is both easier to read and faster than some home-brewed `*s++` version. – Lundin May 03 '18 at 07:53
  • @Lundin not sure this would be so helpful regarding the question, though :) –  May 03 '18 at 08:11
  • All I know is that a newbie spotting your strcat version is just going to stare at it wide-eyed and then hurry back to shallow waters. In general I avoid the `*s1++ = *s2++` pattern for the sake of readability, common as that pattern may be. – Lundin May 03 '18 at 08:17
  • @Lundin On the other hand, without seeing such code and understanding it (cause, the next obvious question would be how to implement `strcpy()`, or `strlen()`), you'll never understand the inner working of strings in C. Whether it is readable or not is probably a matter of taste... –  May 03 '18 at 08:20
  • The inner workings of strcpy() in a professional library would be something revolving around copying chunks of the same size as the CPU word length, with care about alignment and a special case at the end of the string. It will not use `*s1++ = *s2++` unless perhaps it's a standard library for some 8 bit microcontroller. – Lundin May 03 '18 at 08:28
  • @Lundin I know that. I'm talking about educational code here. –  May 03 '18 at 08:28