0

Is the code fine? When I use puts(nstr) in the function I do get the right result , but when on main all I get is "riends" output. using a Microsoft Visual C++ Express if it helps.

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

char* strcpy2 (char* str1, char* str2)
{
    char nstr[20];
    int i,j;
    for (i = 0; str1[i] != '\0'; i++)
    {
        nstr[i] = str1[i];
    }
    for (j = i, i = 0; str2[i]!='\0'; i++, j++)
    {
        nstr[j] = str2[i];
    }
    nstr[j] = '\0';
    return nstr;
}

void main()
{
    char str1[10] = "Hello";
    char str2[10] = ",friends";
    puts(strcpy2(str1, str2));
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Itay Zaguri
  • 43
  • 1
  • 7
  • 5
    You should not return a local (non-static) variable. Read [this](http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope). – 001 Nov 10 '15 at 18:34
  • 1
    Declare nstr[20] in main and pass it in to strcpy2 as the third param – bruceg Nov 10 '15 at 18:34
  • right name should be `strcat2` – fghj Nov 10 '15 at 18:36
  • 1
    The main problem is declaring "char nstr[20]" as a local variable, then passing it outside the function. That's a huge "no-no". A better solution is to declare the buffer *outside* of your function, then pass it in as a parameter. Another problem is the implicit maximum of "20". A better solution is to pass "max size" as a parameter, too. Then modify your loop so it cannot exceed max size. – paulsm4 Nov 10 '15 at 18:38
  • yeah but the point was to use only 2 parameters which are the strings , thanks ! – Itay Zaguri Nov 10 '15 at 18:42
  • `void main(){}` should be at least `int main(void){}` – Michi Nov 10 '15 at 19:05

5 Answers5

5

Summary

Welcome to SO. The main issue is that you're returning the address of a variable that has gone out of scope by the time you try to use it.

Detail

When functions are invoked, local variables are pushed onto a stack (not the data structure!). Here, your local nstr array variable is pushed and is said to be defined within the function's scope.

However, when functions return, local variables are destroyed along with the stack on which they were placed. This means that your nstr variable, has already gone out of scope by the time your function has returned.

This causes your caller, main in this case, to end up with an invalid reference to memory that is no longer in use by the program, which can trigger all sorts of bugs and crashes.

Instead, you should pass a 3rd argument to your function so that it serves as the place where the new concatenated string will go. I don't think making the variable static here is necessary or a good idea. There's no need to keep something in memory when it shouldn't be.

Updated Code

Based on this, your code should look more like this

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

void strcpy2 (char str1[], char str2[], char str3[])
{
    // ...
}

int main(void)
{
    char str1[10] = "Hello";
    char str2[10] = ",friends";
    char str3[20];
    strcpy2(str1, str2, str3);
    puts(str3);
    return 0;
}

You should be able to take it from here.

Update - Why You Should Not Use static Here

Many have recommended using the static keyword here. I advice against this for the following reasons. Using static for your local variable causes your function to be:

  1. Not Thread-Safe: static variables are shared variables;
  2. Not Reusable: function breaks trying to use strings longer than your toy example with a cap of 19+1 chars including null and defeats the purpose of having a function in the first place;
  3. Not Memory Efficient: the static variable will remain in memory with a string that no longer needs to be used until your program exits

You'd have a better-quality function if you pass the 3rd argument as a destination that already has enough space to contain the originals to be concatenated.

You could return a pointer that has been malloc'ed, but note that this is a bit more dangerous and will require more care from everyone using your function. For example, it must be unambiguously clear to every client that it's now their responsibility to free the memory returned by your function. This can be more error-prone because the mallocs are not immediately visible to those using it. (Forgot to free your function's internal/invisible malloc? Whoops! Hello memory leak!).

Using the 3rd param should be safer in general.

You should really take the time to understand the side-effects and consequences that follow from decisions like these.

code_dredd
  • 5,915
  • 1
  • 25
  • 53
  • 1
    There is no `void main(){}` should be at least `int main(void){}` and your code should check if `str1` is long enough to satisfy `str2` and `str3` – Michi Nov 10 '15 at 19:01
  • @Michi: Good catch. It was from the OP's original code paste. Fixed to `int main` and to `return 0` – code_dredd Nov 10 '15 at 19:13
  • you got your `+1` :D. Any way change `int main(){}` void to `int main(void){}` – Michi Nov 10 '15 at 19:17
  • Since `C99` there is no need of `return 0;` because it is implicit, but we do not know which and where it will be used this code. Me, I never ignore things which are optional. in `C89` for example is not optional. – Michi Nov 10 '15 at 19:26
  • 1
    @Michi: I'd like to think that someone learning C *today* would at least be trying to use `C11` :) I also like to be a bit more explicit most of the time. – code_dredd Nov 10 '15 at 19:29
2

nstr is local to function strcpy2. Returning pointer to automatic local variable invokes undefined behavior. Change it to

static char nstr[20]; 

or use malloc to allocate memory dynamically.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • works great, thanks. so that static thing makes the variable accessable out of the function? and why i dont need it when i use arrays? – Itay Zaguri Nov 10 '15 at 18:44
  • @ItayZaguri; [Read this](http://stackoverflow.com/a/572550/2455888). *and why i dont need it when i use arrays?*: you mean dynamic allocation, right? – haccks Nov 10 '15 at 18:51
  • 3
    I think this answer is a bad hack. "It works" is not a good enough reason to use it, and as Martin already pointed out, it's not thread safe either. Also, now this function **cannot** work with any string longer than 19 chars (19 + 1 null). Therefore, it's not reusable at all. – code_dredd Nov 10 '15 at 18:53
  • @ray; *I think this answer is a bad hack.*: the answer shows that you cannot return a pointer to automatic local variable but `static` local variable. Dynamic allocation is also suggested. I am not gonna provide full code for that. *Also, now this function cannot work with any string longer than 19 chars (19 + 1 null). Therefore, it's not reusable at all.*: Its OP responsibility to generalize that function for any length of strings and that's how he will learn. – haccks Nov 10 '15 at 19:04
  • 1
    @haccks: Your explanation does not change the situation. From this, the only deduction I can make is that the OP has learned to 'workaround' the issue by prefixing `static` on to variable names. I agree with you in not providing full code, but no one's asking for that either. – code_dredd Nov 10 '15 at 19:11
  • It's not thinking through the implications that gave us the wonder that is strtok()....:( – Martin James Nov 10 '15 at 23:53
  • @MartinJames: True. There's no evidence here that the OP has any understanding about the consequences of his decision to use `static` here. – code_dredd Nov 11 '15 at 16:48
2

The way you're doing this will not work. For one thing your variable nstr is locally defined and it will not be any good after you exit the function. I could show you how, but I'm guessing you're a student and need to hit the books a little harder. You can do this, it ain't that hard.

Bing Bang
  • 524
  • 7
  • 16
0

I think a pointer with malloc will do the job here, try this:

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

char *strcpy2 (char *str1, char *str2){
    char *result;
    size_t length = 0,i,j;

    length = strlen(str1) + strlen(str2);
    result = malloc(length + 1);

    for (i = 0; str1[i] != '\0'; i++){
        result[i] = str1[i];
    }

    for (j = i, i = 0; str2[i]!='\0'; i++, j++){
        result[j] = str2[i];
    }

    result[j] = '\0';
    return result;
}

int main(void){
    char str1[10] = "Hello";
    char str2[10] = ",friends";

    char *res = strcpy2(str1, str2);
    printf("%s\n",res);

    free(res);
    return 0;
}

Output:

Hello,friends

Michi
  • 5,175
  • 7
  • 33
  • 58
-2

if you print it using printf("%s", strcpy2(str1, str2)); it will be fine. I dont know whats wrong with puts

suspicious
  • 43
  • 1
  • 2
  • 9
  • 2
    What you try looks like it works but this is only by chance, the code is incorrect as you try to access already deallocated memory. This is *undefined behaviour.* In C, something seemingly yielding the right result doesn't mean it's correct. – fuz Nov 10 '15 at 18:54
  • 2
    No. I don't need to compile your faulty code to spot undefined behaviour. You completely miss what's wrong with OPs code. – fuz Nov 10 '15 at 19:02
  • "I don't know what's wrong with `puts`". You should tell your professor your compiler is broken :) – code_dredd Nov 10 '15 at 19:24