-1

Why does strcat gives me its version of str1? As far as I know there has to be & thing before paramatater in function prototype and implementation if you want to get it editted, but I don't see it here.

char *strcat( char *str1, const char *str2 );

How do I edit this function so that it would only return new string but leave out the ones I give it? My try

char *strApp(char *dest, const char *src)
{
    size_t i,j;
    size_t k = 0;
    for (i = 0; dest[i] != '\0'; i++);
    char rdest[100];
    do {
        rdest[k] = dest[k];
    } while(++k<=i);
    for (j = 0; src[j] != '\0'; j++)
        rdest[i+j] = src[j];

    rdest[i+j] = '\0';
    return rdest;
}

It damages second string. Could anyone give me safe and correct version? Thanks in advance.

Brackets
  • 464
  • 7
  • 12

3 Answers3

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

char *strApp(const char *s1, const char *s2)
{
    char *pointer = malloc(strlen(s1) + strlen(s2) + 1);

    if (pointer == NULL)
    {
        perror("failed to allocate memory");
        exit(EXIT_FAILURE);
    } 

    return strcat(strcpy(pointer, s1), s2);
}

int main()
{

    char *s1 = "original";

    char *s2 = " modified";

    char *s3 = strApp(s1, s2);

    printf("%s\n", s1);

    printf("%s\n", s2);

    printf("%s\n", s3);

    free(s3);

    return 0;
}

Just trying to point out you don't need to completely rewrite strcat() to get what you want.

cdlane
  • 40,441
  • 5
  • 32
  • 81
  • Undefined behaviour: you're appending to a buffer that cannot hold any more characters. Not a good example at all. – Sami Kuhmonen Jul 03 '16 at 15:50
  • 2
    @SamiKuhmonen, yes that was a blunder! The type that C happily allows us make. I've replaced the `strdup()` with a proper `malloc()` – cdlane Jul 03 '16 at 15:57
  • error: invalid conversion from 'void*' to 'char*' [-fpermissive] – Brackets Jul 03 '16 at 16:16
  • with strdup: error: invalid conversion from 'size_t {aka unsigned int}' to 'const char*' [-fpermissive] – Brackets Jul 03 '16 at 16:20
  • "void * is automatically and safely promoted to any other pointer type" per [Do I cast the result of malloc?](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). As far as `strdup()`, that was already dealt with two comments prior. – cdlane Jul 03 '16 at 16:32
  • What to do if it does fail to allocate memory? – Brackets Jul 03 '16 at 16:39
  • As written, it exits to the operating system. You originally allocated 100 characters to `rdest` -- if your program can't come up with 100 characters of dynamic memory, you've got bigger problems than `strApp()` so continuing to execute seems a questionable choice. – cdlane Jul 03 '16 at 16:42
  • This needlessly finds the length of the each string twice. The solution I posted addresses this problem. – ikegami Jul 03 '16 at 17:12
  • @cdlane, I'm using Linux, and I just saw the source for `strdup` on Linux, and it doesn't set `errno` explicitly after a failed `malloc`, so I retract my comment. [This comment will self-destruct] – ikegami Jul 03 '16 at 18:02
0

strcat is, by definition, altering the target. If you don't want to, you should make a copy yourself in a target memory location you allocate yourself.

Paul Stelian
  • 1,381
  • 9
  • 27
  • strcpy performs the copy. You still need to allocate a target for the copy large enough to hold both strings and the final NUL terminator. – Paul Stelian Jul 03 '16 at 16:15
0

You've tagged your question with both C and C++. I'm providing a C solution. Adjustments may be needed for C++.


#include <stdlib.h>   
#include <string.h>

char* strdupcat(const char* s1, const char* s2) {
    size_t s1_len = strlen(s1);
    size_t s2_len = strlen(s2);
    char* s = malloc(s1_len + s2_len + 1);

    if (s == NULL)
        return NULL;

    {
        char* s_end = s;
        s_end = mempcpy(s_end, s1, s1_len);
        s_end = mempcpy(s_end, s2, s2_len);
        *s_end = '\0';
    }

    return s;
}

Example usage:

#include <stdio.h>

int main() {
    char* s = strdupcat("abc", "def");
    if (s == NULL) {
        perror("Can't concatenate");
        return EXIT_FAILURE;
    }

    puts(s);
    free(s);
    return EXIT_SUCCESS;
}

This function is used similarly to strdup.

DESCRIPTION

The strdupcat() function returns a pointer to a new string which is a duplicate of the string s1 with a duplicate of string s2 appended. Memory for the new string is obtained with malloc(3), and can be freed with free(3).

RETURN VALUE

The strdupcat() function returns a pointer to the duplicated string, or NULL if insufficient memory was available.

ERRORS

ENOMEM Insufficient memory available to allocate the new string.

You can use strerror or perror to obtain an error message when strdupcat() returns NULL.


Here's a version that accepts an arbitrary number of arguments:

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

char* strdupcatmany(int dummy, ...) {
#define strdupcatmany(...) strdupcatmany(0, __VA_ARGS__, NULL)
    size_t len = 0;
    char* s;
    char* s_dst;
    const char* s_src;
    va_list ap;

    va_start(ap, dummy);
    while (1) {
        s_src = va_arg(ap, const char*);
        if (s_src == NULL)
            break;

        len += strlen(s_src);
    }
    va_end(ap);

    s = malloc(len + 1);
    if (s == NULL)
        return NULL;

    s_dst = s;
    va_start(ap, dummy);
    while (1) {
        s_src = va_arg(ap, const char*);
        if (s_src == NULL)
            break;

        s_dst = stpcpy(s_dst, s_src);
    }
    va_end(ap);
    *s_dst = '\0';

    return s;
}

For example,

#include <stdio.h>

int main() {
    char* s = strdupcatmany("abc", "def", "ghi");
    if (s == NULL) {
        perror("Can't concatenate");
        return EXIT_FAILURE;
    }

    puts(s);
    free(s);
    return EXIT_SUCCESS;
}

Note: I don't know how portable __VA_ARGS__ args is.

Community
  • 1
  • 1
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • And in the place I call strcpycat I also add `free(s);` after it? – Brackets Jul 03 '16 at 17:17
  • Yes, it returns memory allocated by `malloc`, so it needs to be deallocated using `free`. I've added some documentation to my answer. – ikegami Jul 03 '16 at 17:34
  • For the sake of simplicity I used cdlane's version.If I use it like this, will there be a problem? `s4 = strApp(strApp(s1, s2)),s3)`? – Brackets Jul 03 '16 at 18:49
  • One bracket too many, but you get the point. I wonder if second time it will allocate new memory for new variables or not or will it consider the same variables and won't allocate memory because it already got some? – Brackets Jul 03 '16 at 18:55
  • Yeah, memory leak. You have no way of getting the memory allocated by the first call. It's also multiplying the wastefulness – ikegami Jul 03 '16 at 18:57
  • So I can't use it like that? Because as far as I tried it looks ok – Brackets Jul 03 '16 at 19:08
  • Again, it suffers from a memory leak. You need to store the value returned by the inner call so you can call `free` on it – ikegami Jul 03 '16 at 19:10
  • Wait just a minute, first @Brackets says, "I won't need previous result by the time next strApp() will be called" while defending his global variable solution and second, `strdupcat(strdupcat(s1, s2), s3)` has the same memory leak which your comment fails to mention! You can pile up the calls something like: char *s4, *s5; s5 = strApp(s4 = strApp(s1, s2), s3); ...; free(s4); free(s5); – cdlane Jul 03 '16 at 19:11
  • @cdlane, Of course `strdupcat(strdupcat(s1, s2), s3)` has the same problem. He didn't ask about that. – ikegami Jul 03 '16 at 19:49
  • @Brackets, I've added `strdupcatmany` to my answer so you can do `strdupcatmany(s1, s2, s3)` or even `strdupcatmany(s1, s2, s3, s4)`. (I also simplified my original solution my replacing `memcpy` with `mempcpy`.) – ikegami Jul 03 '16 at 19:49