0

I am encountering problems in understanding the static identifier in the specific case of this program. The context of this program is a lecture about "pointers".

This program's job is to create a new name for a temporary file, it accomplish this (with the help of static int sequence) by creating a new name every time the function tmp_name is called.

  1. first call –> "tmp0"
  2. second call –> "tmp1"
  3. ...

[code]

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

/*******************************************************
 * tmp_name -- Return a temporary filename             *
 *                                                     *
 * Each time this function is called, a new name will  *
 * be returned.                                        *
 *                                                     *
 * Returns                                             *
 *      Pointer to the new filename                    *
 *******************************************************/
char *tmp_name(void){

    static char name[30]; /* The name we are generating */
    static int sequence = 0; /* Sequence number for last digit */

    ++sequence; /* Move to the next filename */

    strcpy(name, "tmp");

    name[3] = sequence + '0';

    /* End the string */
    name[4] = '\0';

    return(name);
}

int main(){

    char *tmp_name(void); /* Get the name of temporary file, very first call */
    char *name1; /* Name of a temporary file, second call */
    char *name2; /* Name of another temporary file, third call */

    name1 = tmp_name();
    name2 = tmp_name();

    printf("Name1: %s\nName2: %s\n", name1, name2);
    return(0);
}

So the output of the program (since the printf function is called) is to print "tmp1" and "tmp2" ("tmp0" cannot be printed, and it is perfectly ok).

So the program is PERFECTLY WORKING, what is the problem then? The problem is that if i remove static from static char name[30] the program breaks. It prints this out:

Name1: \340\364\277\357\376
Name2: \340\364\277\357\376

I studied what static does mean and what it implies, for this reason the use of static int sequence is perfectly clear for me, but i really cannot understand why also the array [name] is declared statically.

scugn1zz0
  • 301
  • 1
  • 6
  • 15
  • It doesn't have to be, a target array could have been passed by the caller, in a different version. And if several filenames need to be collected, that would have to be the way to do it, because the static array in the function gets overwritten. – Weather Vane Mar 02 '18 at 14:45
  • Note that `tmp_name()` is not thread safe, and not reentrant. So it's bad practice to do that. – Iharob Al Asimi Mar 02 '18 at 14:47
  • 1
    [There](https://stackoverflow.com/q/28864924) [are](https://stackoverflow.com/q/28679558) [very](https://stackoverflow.com/q/22288871) [many](https://stackoverflow.com/q/12380758) [questions](https://stackoverflow.com/q/6897914) about returning the address of a local array –  Mar 02 '18 at 14:53
  • @lharob: I agree it is currently thread-bad, but perhaps by stopping using the static buffer and then handling the increment, that is exactly what scugn is trying to fix :-) – Gem Taylor Mar 02 '18 at 15:48

3 Answers3

1
char *stuff() {
    char thing[100];
    strcpy(thing, "what are you returning??");

    return thing;
}

Now, what are you returning here?? A pointer. To what? Remember that non-static local variables are destroyed after the function returns. So, in char *data = stuff(); the variable data will be full of garbage since thing has been disposed of.

Static variables, however, are still there and are perfectly valid even when the function exits, so that pointer you're returning still points to the memory your program owns and that definitely contains the data you put there, while in the case of non-static variables, you lose control of this memory once the function exits, and thus any program may put anything in there, that's how you get this garbage data.

ForceBru
  • 43,482
  • 10
  • 63
  • 98
1

The problem is that if i remove static from static char name[30] the program breaks.

If you remove static for name, it becomes an object with automatic storage duration and returning it (i.e., pointer to a local variable) results in undefined behaviour. With static it works because name will have static storage duration (i.e., name object is alive throughout the execution of the program).

Alternatively you can allocate memory dynamically (e.g., using malloc) and copy the name into it and return it.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • Or a much, much better solution is to pass the array from the caller. – Iharob Al Asimi Mar 02 '18 at 14:48
  • I wouldn't say it's "much better" but just another alternative. It's entirely upto the use case in question. For example, if each of such "names" need to be preseved, then returning malloc'ed value becomes easier (to save). It's even OK to use `static` as it is if the function isn't required to be re-entrant. – P.P Mar 02 '18 at 14:55
  • No dynamic allocation means no need to `free()`. – Iharob Al Asimi Mar 02 '18 at 15:01
  • If not free'ing is such an important advantage, no one would use `malloc` & friends ;-) As I said, what's "best" is entirely up to the use case. Judging by OP's description, there's not much of an use case except for their own understanding. – P.P Mar 02 '18 at 15:03
  • I don't know who downvoted and it bugs me too really. They don't give a reason and sometimes they don't give time to edit in initial 1 min. I didn't downvote you. But yes I will upvote because what you said works and it is right. I know how it feels when you are DVed without a reason. – user2736738 Mar 02 '18 at 15:47
1

Let's start with the basics: §5.2.3 standard N1570

The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,33) and retains its last-stored value throughout its lifetime.34) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

The italics present a nice idea - what it has to do with anything else? For static storage duration

An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.

So that means when you write static somevar it will live until the program ends in this case until main() ends. The storage duration of static variable is beyond the scope of the function. So returning a pointer pointing to it is valid. And it won't be illegal code.

Now let's see another

An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration, as do some compound literals.

That means when you remove the static from the variable name in this case and it is in enclosing block of the function - it is automatic storage duration.

And then comes the most important part in explaining why the second one fails

For such an object that does have a variable length array type, its lifetime extends from the declaration of the object until execution of the program leaves the scope of the declaration.

such an object means here those with automatic storage duration. And as you see here when the enclosing block the function body ends - the value of pointer that you return from the function becomes indeterminate and accessing it is undefined behavior(Check the first paragraph).

This explains why you see what you see.(This is what you asked - what is happening and why is it so?) This is explanation standard wise for the given behavior.

user2736738
  • 30,591
  • 5
  • 42
  • 56