Oleksiy
Oleksiy

Reputation: 39810

How does static work inside a function?

I came across something I haven't seen before. I have the following recursive function that only works when i is static

void printNthFromLast(Node* head, int n) {

    static int i = 0;

    if(head == nullptr)
        return;

    printNthFromLast(head->next, n);

    if(++i == n)
        cout << head->data;
}

I assume static in this context means shared among multiple recursive calls to the same function printNthFromLast?

The real confusing part is that it doesn't reinitialize it to 0 every time the recursive function calls itself. It's like this whole line of code static int i = 0; is skipped?

Can someone explain this to me?

Upvotes: 0

Views: 172

Answers (7)

Matthieu M.
Matthieu M.

Reputation: 299890

So, as said, static int i = 0; is local to the function. It is initialized the first time flow-control passes through its definition, and skipped ever after. Two special cases are made:

  • in case of dynamic initialization (by a function) which throws an exception, initialization will be reattempted the next time flow-control passes through the definition.
  • in case of calls from multiple threads, the first thread does the initialization and all others are blocked until it is done (or fails with an exception)

Now, regarding your code: don't. A local static is a global variable in disguise, your code is equivalent to:

int i = 0;

void printNthFromLast(Node* head, int n) {    
    if(head == nullptr)
        return;

    printNthFromLast(head->next, n);

    if(++i == n)
        cout << head->data;
}

Note: not only is it more difficult to debug, it is also not thread-safe.

Instead, you should provide a local variable for this usage:

static void printNthFromLastImpl(Node const* const head, int& i, int const n) {
    if(head == nullptr)
        return;

    printNthFromLastImpl(head->next, i, n);

    if(++i == n)
        cout << head->data;
}

// Each call to this function instantiates a new `i` object.
void printNthFromLast(Node const* const head, int const n) {
    int i = 0;
    printNthFromLastImpl(head, i, n);
}

EDIT: As remarked by Ad N, since the list is not modified it should be passed by const pointer.

Following Ad N latest edit (TCO version), I realized an iterative implementation should work (note, there might be some off by one errors).

void printNthFromLast(Node const* const head, int const n) {
    if (n == 0) { return; }

    // Set "probe" to point to the nth item.
    Node const* probe = head;
    for (int i = 0; i != n; ++i) {
        if (probe == nullptr) { return; }
        probe = probe->next;
    }

    Node const* current = head;

    // Move current and probe at the same rhythm;
    // when probe reaches the end, current is the nth from the end.
    while (probe) {
        current = current->next;
        probe = probe->next;
    }

    std::cout << current->data;
}

Upvotes: 1

Ad N
Ad N

Reputation: 8386

The behavior you observed :

The real confusing part is that it doesn't reinitialize it to 0 every time the recursive function calls itself. It's like this whole line of code static int i = 0; is skipped?

is exactly what you are asking for with static. A local static variable is initalized the first time its definition (i.e, the line static int i = 0;) is reached.

In your case it means it will be set to zero only on the first call ever to this method during the whole runtime of your program. There is no notion of multiple recursive calls to the same function, so it will make no difference if the method is invoked by itself (the multiple recursive call you are referring to) or if you are starting a whole new stack of recursion somewhere else in your client code.

Possible solution

To go back on your description, it will only work with i being static (for n!=1) because, if your removed static keyword, then i would be initialized to zero each time the method is entered (a different local instance of i for each invocation of the method). Thus in your condition

  if(++i == n)

++i would always evaluate to 1, independently of the recursion depth.

If you want the static i to be reinitialized each time you call your method in your client code (i.e. to start a new stack of recursion), you could write it like :

void printNthFromLast(Node* head, int n, bool reinit=true)
{

   static int i = 0;

   if(reinit)
   {
       i=0;
   }

   if(head == nullptr)
      return;

   printNthFromLast(head->next, n, false);

   if(++i == n)
       cout << head->data;
}

Thread safe with tail call optimization

A cleaner solution would be to have i as a mutable parameter to the function, so your function would be thread-safe. And with a better ordering of the operation, you do not need to save the previous invocation frame, thus enjoying tail call optimization offered by most of recent compilers.

EDIT : As pointed by Matthieu, my original implementation printed the Nth element instead of the Nth from last element. Fixing while enabling TCO is less elegant, but can be done as follows :

/// n==1 means printing the last element here
static void printNthFromLastImpl(const Node* currentNode, const Node* offsetNode, const int n, int currentDepth)
{
    // n == 0 will never print in the original example by op, so forbid it
    assert(n);
    
    if(++currentDepth > n)
    {
        offsetNode = offsetNode->next;
    }
    
    if(currentNode->next)
    {
        currentNode = currentNode->next;
    }
    else
    {
        if(currentDepth >= n)
        {
            cout << offsetNode->data;
        }
        return;
    }
  
  
    return printNthFromLastImpl(currentNode, offsetNode, n, currentDepth);
}

/// \brief : code to call from client
static void printNthFromLast(const Node* head, const int n)
{
    if (head)
    {
        printNthFromLastImpl(head, head, n, 0);
    }
}

Upvotes: 2

Jon
Jon

Reputation: 437386

When you declare a static local variable the compiler only initializes it once (the first time control flow passes over the variable declaration); thereafter the variable keeps its value across all calls to the function that declares it for the lifetime of the program, much like a global variable.

How is this achieved?

When the compiler sees something like this:

void foo() {
    static int i = 0;

    cout << i++;
}

It produces code equivalent to this:

bool foo_i_initialized = false; // global
int foo_i; // global

void foo() {
    if (!foo_i_initialized) {
        foo_i = 0;
        foo_i_initialized = true;
    }

    cout << foo_i++;
}

The above example is a bit contrived because here foo_i is a primitive initialized with a constant and as such it could have been statically initialized in global scope (removing the need for the boolean flag), but this mechanism also handles more complicated scenarios.

Upvotes: 2

Some programmer dude
Some programmer dude

Reputation: 409176

Not only shared among multiple recursive calls, shared among all calls.

There is actually only a single instance of the variable i, and it's shared among all calls of the function.

Upvotes: 0

Bhargav Ponnapalli
Bhargav Ponnapalli

Reputation: 9412

A variable declared static is initialized only once. So even, when the function is called again, the value of the variable i here in this context is retained from the previous call.
The next time the program reaches the static initialize statement to an initialized variable, it skips the statement. Since, static variables are stored in the BSS segment and not on the stack, the previous state of the variable in previous function calls, is not altered.

Upvotes: 0

user2656151
user2656151

Reputation:

The initialization is only executed at the first call of the function. Subsequent calls will use the already inititalized value.

Upvotes: 1

RichieHindle
RichieHindle

Reputation: 281495

You've described it very well yourself. A static variable is initialised only once, the first time through the function, and the variable is then shared across calls to the function.

Upvotes: 0

Related Questions