-1

I'm in the process of teaching myself C and I'm mistified as to what's causing the following issue: when I create an array in a method and return it as a pointer to the calling function, none of the content is correct. I've boiled down this problem to the following example:

char * makeArr(void){
    char stuff[4];
    stuff[0]='a';
    stuff[1]='b';
    stuff[2]='c';
    stuff[3]='d';
    printf("location of stuff:%p\n",stuff);
    int i;

    for(i = 0; i < 4; i++){
       printf("%c\n",stuff[i]);
    }

    return stuff;
}

int main(void){
    char* myarr;
    myarr = makeArr();
    int i;
    printf("\n");
    printf("location of myarr:%p\n", myarr);
    for(i = 0; i < 4; i++){
        printf("%c\n",myarr[i]);
    }
}

The output returns the following:

location of stuff:0028FF08
a
b
c
d

location of myarr:0028FF08
Ä
ÿ
(
(a null character)

So I've verified that the locations between the two values are the same, however the values differ. I imagine that I'm missing some critical C caveat; I could speculate it's something to do with an array decaying into a pointer or a problem with the variable's scope, but and any light that could be shed on this would be much appreciated.

schil227
  • 292
  • 3
  • 11
  • 2
    `stuff` is located on the local stack and goes out of scope. Please delete this question as it is a duplicate. – John3136 Feb 27 '15 at 02:26
  • 1
    possible duplicate of [Returning an array using C](http://stackoverflow.com/questions/11656532/returning-an-array-using-c) – John3136 Feb 27 '15 at 02:27
  • "... or a problem with the variable's scope ..." That's a bingo! You are actually lucky that the string got trashed when the function returned. Sometimes, the data might be left unmolested (still wrong to look at it though!) and you wouldn't have even realized there was a problem. :P – jschultz410 Feb 27 '15 at 02:36

3 Answers3

4

What you're attempting to do is return the address of a local variable, one that goes out of scope when the function exits, no different to:

char *fn(void) {
    char xyzzy = '7';
    return &xyzzy;
}

That's because, other than certain limited situations, an array will decay into a pointer to the first element of that array.

While you can technically return that pointer (it's not invalid in and of itself), what you can't do is dereference it afterwards with something like:

char *plugh = fn();
putchar (*plugh);

To do so is undefined behaviour, as per C11 6.5.3.2 Address and indirection operators /4 (my bold):

If an invalid value has been assigned to the pointer, the behaviour of the unary * operator is undefined.

Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.

Having stated the problem, there are (at least) two ways to fix it.

First, you can create the array outside of the function (expanding its scope), and pass its address into the function to be populated.

void makeArr (char *stuff) {
    stuff[0]='a';
    stuff[1]='b';
    stuff[2]='c';
    stuff[3]='d';
}

int main(void) {
    char myarr[4];
    makeArr (myarr);
    // Use myarr here
}

Second, you can dynamically allocate the array inside the function and pass it back. Items created on the heap do not go out of scope when a function exits, but you should both ensure that the allocation succeeded before trying to use it, and that you free the memory when you're finished with it.

char *makeArr (void) {
    char *stuff = malloc (4);
    if (stuff != NULL) {
        stuff[0]='a';
        stuff[1]='b';
        stuff[2]='c';
        stuff[3]='d';
    }
    return stuff;
}

int main(void) {
    char *myarr;
    myarr = makeArr();
    if (myarr != NULL) {
        // Use myarr here
        free (myarr);
    }
}
Community
  • 1
  • 1
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • @jschultz410, actually, he calls `printf` on each _character,_ rather than treating it as a string, so no NUL is required. – paxdiablo Feb 27 '15 at 02:43
  • Wow, this is an awesome answer! Thanks very much for the thorough detail, this really helps when starting out! – schil227 Feb 27 '15 at 03:30
  • 1
    @schil227: copious amounts of detail is my specialty, though my wife would say it's just a deep-seated desire to not shut up :-) – paxdiablo Feb 27 '15 at 03:32
2

stuff[] only exists on the stack during function call, it gets written over after return. If you want it to hold values declare it static and it will do what you want.

However, the whole idea is fundamentally lame, don't do that in real life. If you want a function to initialize arrays, declare an array outside of the function, pass a pointer to this array as a parameter to the function and then initialize an array via that pointer. You may also want to pass the size of the array as a second parameter.

Since you're learning, a sample code is omitted intentionally.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Oleg Mazurov
  • 497
  • 5
  • 10
  • I would just add to that first paragraph that `static` can introduce problems for threading or re-entrant code. – paxdiablo Feb 27 '15 at 02:58
1

Your array stuff is defined locally to the function makeArr. You should not expect it to survive past the life of that function.

char * makeArr(void){
  char stuff[4];

Instead, try this:

char * makeArr(void){
  char *stuff=(char*)calloc(4, sizeof(char));

This dynamically creates an array which will survive until you free() it.

Richard
  • 56,349
  • 34
  • 180
  • 251