life time and scope in static variable

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have doubt on static variable in c language As I know static variable stay alive while program is running. if static variable is used in function (static local variable) What will be duration of variable. Will variable create and exit in each function call. I think static variable only initialize once it will not create and exit at each function call.  What is  life time of  static variable in c language ?

This topic has a solution.
Last Edited: Thu. Feb 6, 2020 - 09:52 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

static locals are stored (globally) in BSS so their duration is for the entire life of the program

 

I posted something about this with a simple example in the last week or so. (except I can't find it now :-(

Last Edited: Thu. Feb 6, 2020 - 04:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ansh11 wrote:
I think static variable only initialize once it will not create and exit at each function call

Correct.

 

You can, for example, use a static local within a function to count how many times that function has been called

 

void counter( void )
{
    static int count;

    ++count;

    printf( "This function has been called %d time(s)\n", count );
}

 

It is very common to us a static local to save "state" between calls to a function 

 

EDIT

 

typo - "withing"

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 6, 2020 - 09:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

 

You can, for example, use a static local withing a function to count how many times that function has been called

 

void counter( void )
{
    static int count;

    ++count;

    printf( "This function has been called %d time(s)\n", count );
}

 

It is very common to us a static local to save "state" between calls to a function 

What happens if I call function counter one times from main function.  How long does a variable count alive?  As long as the program runs or until the function exit. it means if the function is exit then the variable will also be destroyed. I am not sure what happens exactly 

Last Edited: Thu. Feb 6, 2020 - 09:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ansh11 wrote:
What happens if I call function counter one times from main function.  How long does a variable count alive?

Cliff answered your question already!

clawson wrote:
static locals are stored (globally) in BSS so their duration is for the entire life of the program

It will hold it's value until power is removed, or the reset button is pressed.  Then it will be set to it's initial value again.

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ansh11 wrote:
How long does a variable count alive?  As long as the program runs or until the function exit.

clawson answered that already.

 

The code I posted illustrates what he said.

 

Think about it: if it only lived until the function exits, then it wouldn't be able to retain its value between calls - would it?

 

Note that this is all standard 'C' stuff - not specific to AVR

 

http://c-faq.com/sx1/index.html#static

 

https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.cbclx01/statdef.htm

 

https://en.cppreference.com/w/cpp/language/storage_duration

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 6, 2020 - 09:52 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

It would take you but a few minutes to write a small test program with which you can experiment to your heart's content.

 

This behaviour is most easily tested using a C compiler for your PC but a second best would be the simulator in Atmel Studio with the caveat that you cannot printf to the debug console.

 

However, in the simulator you CAN use a complex breakpoint {add a breakpoint - then click settings to "Log a message to Output Window"} at the line where you need to inspect a variable and the operation is then very similar if not identical to that in Microsoft Visual Studio.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
It would take you but a few minutes to write a small test program with which you can experiment to your heart's content.

You'd just need a main() to call the example function I showed in #3.

 

I just made that up for the post;  but it turns out to be pretty much the standard example - see the links in #6

 

Here's another:  https://www.learncpp.com/cpp-tutorial/static-local-variables/

 

This behaviour is most easily tested using a C compiler for your PC

Indeed.

 

 

 the simulator in Atmel Studio with the caveat that you cannot printf to the debug console.

You can just set a 'watch' on the variable - and see it change (or not) as you step through the code ...

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 6, 2020 - 10:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
static locals are stored (globally) in BSS so their duration is for the entire life of the program
More precisely:

Static locals exist for the entire life of the program,

so the static locals initialized to zero are placed

in the .bss section by toolchains that have a .bss section.

 

IIRC static locals are not actually required to exist until they first come in scope.

That said, when it comes to allocating memory,

that little caveat does not make much difference.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

IIRC static locals are not actually required to exist until they first come in scope.

That said, when it comes to allocating memory,

that little caveat does not make much difference.

I think in C++, a static local object's constructor is called the first time the object comes into scope, which definitely can make a difference with the behavior of a program if the constructor does anything besides set the state of the object itself. But C doesn't have object constructors as in C++, so there's no observable difference when a static local begins to exist (does a tree falling in a forest make a sound if no one is there to hear it?).

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

 

clawson wrote:
I posted something about this with a simple example in the last week or so. (except I can't find it now :-(
As the search here is rubbish (and even google didn't help much) I figure it's easier to just re-post something similar to what I had previously. So in Visual Studio 17 just now I wrote:

#include <windows.h>
#include <stdio.h>
#include <stdint.h>

int count() {
    static int counter = 17;
    return counter++;
}

int main(void) {
    printf("count = %d\n", count());
    printf("count = %d\n", count());
    printf("count = %d\n", count());
    printf("count = %d\n", count());
    return 0;
}

and when I build and run that the result is:

 

 

So I think it's pretty clear what's going on. "counter" doesn't just exist for the duration of the count() function. It is held in global memory so that when control returns to count() it continues with the value it was last set to.

 

In effect it's not entirely different to:

int counter = 17;

int count() {
    return counter++;
}

However the one things that is different is "name scope". So if in this version I did:

int main(void) {
    counter = 32; // added this
    printf("count = %d\n", count());

then the counting sequence output would have been:

So main() was allowed to "mess with" a variable that is "owned" by count(). By making it "static" within the function it limits its name scope so nothing outside count() knows it even exists and if I added that "counter = 32;" line to main() in this case I couldn't even build the code:

    testfp.cpp
    d:\c\testfp\testfp\testfp.cpp(11): error C2065: 'counter': undeclared identifier

because "counter" cannot be seen by main() in this case (it is not "global"). 

 

If I had 5 different functions that all kept a "counter" then I could happily build the code as long as each were static within the various functions to limit their name scope. But if it were global they'd all be fighting to use the same variable.

 

As such there is a strong argument for making things static when you can (they don't need to be accessed "outside" the function where they occur but you do want their value retained from one invocation to the next). C++ takes this to the next level with "private:" class members.

 

BTW it took me about 2 minutes to try this out in VS2017. I would suggest you get a copy and you can run "C learning" experiments like this yourself.

 

As this is about C programming and nothing specifically to do with AVRs it is about 1,000,000 times easier to try experiments like this without involving an AVR or Atmel Studio or whatever.

 

Once you have learned C then take the knowledge back to your AVR projects.

 

Last Edited: Fri. Feb 7, 2020 - 11:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Aha - another example with the counter!

 

awneil wrote:
I just made that up for the post;  but it turns out to be pretty much the standard example

 

As this is about C programming and nothing specifically to do with AVRs it is about 1,000,000 times easier to try experiments like this without involving an AVR or Atmel Studio or whatever.

 

Once you have learned C then take the knowledge back to your AVR projects.

Absolutely!

 

 

PS

 

I was going to say "counter example" - but then realised that means something different!

 

laugh

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Fri. Feb 7, 2020 - 12:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff, in your first example, will the count variable show up in the lss/map file? and thus as such will it be accounted for if you look at the used memory figure at the end of the build?

I do not have a machine with either studio or other programming tool at the moment, and was indeed wondering at first what the difference would be in declaring variables outside a routine, but not making them public, and also if the memory usage is visible in the end.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nope the .map file only documents "global" items so "counter" will use two bytes of memory but you won't see it named in the .map file. If I build a slightly more "AVR" version of that code in Atmel Studio 7:

#include <avr/io.h>

int count() {
	static int counter = 17;
	return counter++;
}

int main(void) {
	(void)count();
	(void)count();
	(void)count();
	(void)count();
	return 0;
}

then when I build it:

		"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-size.exe" "test.elf"
		   text	   data	    bss	    dec	    hex	filename
		    112	      2	      0	    114	     72	test.elf
	Done executing task "RunCompilerTask".
	Using "RunOutputFileVerifyTask" task from assembly "C:\Program Files (x86)\Atmel\Studio\7.0\Extensions\Application\AvrGCC.dll".
	Task "RunOutputFileVerifyTask"
				Program Memory Usage 	:	114 bytes   1.4 % Full
				Data Memory Usage 		:	2 bytes   0.4 % Full

You can see both memory displays here admitting to the two byte "int" but the .map file:

.data           0x00800060        0x2 load address 0x00000070
                0x00800060                PROVIDE (__data_start, .)
 *(.data)
 .data          0x00800060        0x2 main.o
 *(.data*)
 *(.gnu.linkonce.d*)
 *(.rodata)
 *(.rodata*)
 *(.gnu.linkonce.r*)
                0x00800062                . = ALIGN (0x2)
                0x00800062                _edata = .

admits that 0x02 bytes were allocated from main.o to occupy 0x60/0x61 but the symbol is not named as "counter". If I move it out of the function and make it a global the difference is:

.data           0x00800060        0x2 load address 0x00000070
                0x00800060                PROVIDE (__data_start, .)
 *(.data)
 .data          0x00800060        0x2 main.o
                0x00800060                counter

so now the "counter" name is publicized. 

 

If I did not have the initialization to the value 17 then it would have been (in either case):

.bss            0x00800060        0x2
                0x00800060                PROVIDE (__bss_start, .)
 *(.bss)
 *(.bss*)
 *(COMMON)
 COMMON         0x00800060        0x2 main.o
                0x00800060                counter
                0x00800062                PROVIDE (__bss_end, .)

so it moved from ".data" to "COMMON" (effectively .bss) in the global case and:

.bss            0x00800060        0x2
                0x00800060                PROVIDE (__bss_start, .)
 *(.bss)
 .bss           0x00800060        0x2 main.o
 *(.bss*)

just plain .bss in the static case.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
.map file only documents "global" items

Is there an option to change that?

 

I expect something like objcopy or objdump could be persuaded to show the details ...

 

EDIT (which was going to be in reply to a post which has now disappeared)

 

in the debug info ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Mon. Feb 10, 2020 - 03:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would have said the ELF does not contain the "private" symbol but I just proved myself wrong with:

#include <avr/io.h>

int a_global;

int count() {
	static int counter = 17;
	return counter++;
}

int main(void) {
	(void)count();
	(void)count();
	(void)count();
	(void)count();
	return 0;
}

I was full expecting to see "a_global" but thought there simply would not be a mention of "counter" in the ELF. However:

D:\test\test\Debug>avr-nm test.elf
00000048 t .do_clear_bss_loop
0000004a t .do_clear_bss_start
00000054 T __bad_interrupt
00800064 B __bss_end
00800062 B __bss_start
0000001e T __ctors_end
0000001e T __ctors_start
00800062 D __data_end
00000082 A __data_load_end
00000080 A __data_load_start
0000ffa0 A __DATA_REGION_LENGTH__
00800060 A __DATA_REGION_ORIGIN__
00800060 D __data_start
00000040 T __do_clear_bss
0000002a T __do_copy_data
0000001e T __dtors_end
0000001e T __dtors_start
00810000 N __eeprom_end
00010000 A __EEPROM_REGION_LENGTH__
00000003 A __FUSE_REGION_LENGTH__
00000000 W __heap_end
0000001e W __init
00000400 A __LOCK_REGION_LENGTH__
00000400 A __SIGNATURE_REGION_LENGTH__
0000003e a __SP_H__
0000003d a __SP_L__
0000003f a __SREG__
0000025f W __stack
0000007e t __stop_program
00002000 A __TEXT_REGION_LENGTH__
00000000 A __TEXT_REGION_ORIGIN__
00000000 a __tmp_reg__
0000001e T __trampolines_end
0000001e T __trampolines_start
00000400 A __USER_SIGNATURE_REGION_LENGTH__
00000054 W __vector_1
00000054 W __vector_10
00000054 W __vector_11
00000054 W __vector_12
00000054 W __vector_13
00000054 W __vector_14
00000054 W __vector_2
00000054 W __vector_3
00000054 W __vector_4
00000054 W __vector_5
00000054 W __vector_6
00000054 W __vector_7
00000054 W __vector_8
00000054 W __vector_9
00000000 W __vector_default
00000000 T __vectors
00000001 a __zero_reg__
00800062 D _edata
00800064 N _end
00000080 T _etext
0000007c T _exit
00800062 B a_global
00000056 T count
00800060 d counter.1604
0000007c W exit
0000006e T main

(I did have to switch off -gc-sections to prevent "a_global" being eaten!)

 

So both are there though I'm not yet sure what the significance of the ".1604" appended to "counter" is yet.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PS just reading the NM manual:

 

https://sourceware.org/binutils/docs-2.33.1/binutils/nm.html

 

So the upper/lower case thing in the above is because of:

The symbol type. At least the following types are used; others are, as well, depending on the object file format. If lowercase, the symbol is usually local; if uppercase, the symbol is global (external). There are however a few lowercase symbols that are shown for special global symbols (uv and w).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
 I'm not yet sure what the significance of the ".1604" appended to "counter" is 

Being local, It's possible that there might be many local things called "counter" - so I guess it needs some way to tell them apart ... ?

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
It's possible that there might be many local things called "counter" - so I guess it needs some way to tell them apart ... ?
name mangling in C rather than C++? Interesting.

 

Quick experiment:

int count() {
	static int counter;
	return counter++;
}

int count2() {
	static int counter;
	return counter++;
}

yields:

00000040 T count
00000058 T count2
00800062 b counter.1604
00800060 b counter.1607

wonder if the 04 and 07 bits are something to do with line numbers (except they are NOT the right line numbers) but wonder what the 16 is? bit width perhaps?

 

(EDIT numbers did not change when I changed them from "int" to "long" so guess that rules out the bit width guess!)

Last Edited: Mon. Feb 10, 2020 - 03:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the clarification.

Like you I would have expected that putting the definition inside a function would make it anonymous, and thus hardly traceable. Thus that putting it outside a function would be easier if you were looking at something.

A second thing that popped up is what happens when you go debugging and add watches to the variables.