0

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?

Oleksiy
  • 37,477
  • 22
  • 74
  • 122
  • 2
    At some point, it will be more productive to bite the bullet and read [some good C++ books](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). – juanchopanza Aug 07 '13 at 09:16
  • @juanchopanza I already looked at that and I'm planning to read the book by Stroustrup as soon as I have time. Would you recommend that I read a book on C first? – Oleksiy Aug 07 '13 at 09:21
  • @juanchopanza, I could not agree more with you, yet I answered the question just for the easy rep points... Anyway, you inded described the basic behavior of the keyword static, so any ressource regarding C++ would have given you this answer ; ) – Ad N Aug 07 '13 at 09:27
  • I don't think a C book is necessary, a *good* C++ book should tell you what you need to know without teaching you idioms that work well in C but have better alternatives in C++. It may be worth learning C, but that could be done at a later stage (but this is my opinion, and it is a controversial topic). – juanchopanza Aug 07 '13 at 09:29

7 Answers7

2

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);
    }
}
Community
  • 1
  • 1
Ad N
  • 7,930
  • 6
  • 36
  • 80
  • I cannot, in good conscience, accept such a hackish solution; especially when you actually hint at the *right* solution at the end. Beginners seek advice here, let us give them *good* advice. – Matthieu M. Aug 07 '13 at 09:41
  • The latter function is much cleaner, but unfortunately erroneous. It prints the `nth` node from the start while it should print the `nth` from the end (I had missed it too, at first). Because of this peculiar quirk, I know not how to structure the code to trigger TCO here (without altering the data structure). – Matthieu M. Aug 07 '13 at 11:58
  • @Matthieu : My bad indeed, I am not implementing the required function here ! And in essence, it seems we need to save the previous invocation frames, to unwind them (or at least n of them) when we reach the end. So there is no cheap TCO at hand, I was wrong. There is 'of course' the solution to pass a pointer to the '-n' data when invoking the method, but then it will depend on the use case to chose if it worth the effort (since it will require to manage more arguments)... – Ad N Aug 07 '13 at 12:12
  • @MatthieuM.: Here the method is implemented to enable TCO while printing the Nth from last element data. It is less elegant, but would allow to avoid stack overflows for deep trees. – Ad N Aug 07 '13 at 14:12
  • Indeed (took me a couple seconds to get it). At this point we are getting quite close to an iterative implementation. – Matthieu M. Aug 07 '13 at 14:17
2

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.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Note: actually, the `foo_i_initialized = true` should be executed after initialization. In more complex cases (init by function which may throw), it is only executed if the function call succeeds and the object is correctly initialized. – Matthieu M. Aug 07 '13 at 09:40
1

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

1

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;
}
Community
  • 1
  • 1
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Thank you!! I didn't know it wasn't thread safe. Looks a lot cleaner now – Oleksiy Aug 07 '13 at 09:49
  • I cannot, in good conscience, accept such solution; especially when you were kind enough to explain to me that beginners seek good advice here. The signature of your function does not enforce const correctness, and also tend to indicate that you should pass arithmetic built-in types by reference when what you really want here is a copy. Also, a better ordering of instructions would give you TCO for free. – Ad N Aug 07 '13 at 10:03
  • @AdN: I agree with const-correctness, but disagree on the other remarks as they would change the semantics. – Matthieu M. Aug 07 '13 at 11:54
  • @MatthieuM.: Indeed, I was wrong implementing the method to print the Nth element, so you need to pass a reference to the i counter and your approach is correct. But you should still be able to make n const I guess. – Ad N Aug 07 '13 at 14:10
  • @AdN: Added, I also added an iterative version. It's longer than the TCO version but might be easier to grok. – Matthieu M. Aug 07 '13 at 14:23
  • @MatthieuM.: Indeed easier to understand ! You are not off by one, but you should make sure n!=0 (or this will jump over your `for`, and try to access ->data on the nullptr). – Ad N Aug 07 '13 at 14:31
0

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.

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
0

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.

Bhargav Ponnapalli
  • 9,224
  • 7
  • 36
  • 45
0

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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621