How (embedded) C code works.

Let's walk through what happens when power is applied to a microcontroller.

First, power comes up and a circuit places the device in reset by holding the reset
line low for a brief period.  In keeping with AVR's "Just come up and go" philosophy,
there is an internal circuit that does this for you.  There is also an external pin.

The processor gets to the starting location of your code from a jump instruction in the
interrupt/reset vector table.  See the full (not summary) data sheet for your favorite
part and look under Interrupts, subsection Interrupt vectors.  The vector table
holds jump instructions, not addresses.  Remember, this is a Modified Harvard Architecture
processor and has separate data and instruction busses and memory spaces.  Different
size processors will have one or two instructions per table entry.

There will be some startup code at this point which does things like set the stack pointer,
turn on the interrupt enable flag, and initialize data.

Technical details of memory initialization appear here on the nongnu.org website.

When you declare a file-scope static variable in C that is initialized and that variable
is not a const variable, The variable is placed in RAM and initialized.

Let me illustrate:


int x=5;

int main(int argc, char *argv[]) {

    int y=6;
    static int z=7;
    char *str = "testing";

    while (1) {
        /* your code goes here */
    }

}
Remember, that in C, local variables are held on the stack.  This means that when a
variable is declared, the variable is given space by the compiler to exist in the stack.
When the function exits, that variable is gone (its lifetime is over).  That's why
you never want to return a pointer to a locally declared variable in a function.  When the
function exits, the stack frame that holds the variable is gone, and the pointer points to
whatever may be in whatever stack frame is using that memory at the time.

In this case, the y=6 is allocated in the stack frame and initialization code is put at the beginning 
the function to set the initial value to 6.

Now, notice that x is not declared inside a function.  It's not a local (or, technically an automatic)
variable (see Kergnihan & Ritchie for details).  These variables are placed in a segment by the linker,
and there they sit for the entire time the processor runs.  (This would not be exactly true in larger
embedded systems with an operating system that supports processess, but we're talking about
small-processor, embedded C here.)

So, we have what's called a "file scope" variable.  It's global,  and never "disappears" by going out
of scope when a function ends.

Now, a good question to ask would be, how does the 5 get put into x?  The CPU starts up, and the
RAM is in an indeterminate state.  There's a space the right size for an int in RAM, but we need to
initialize it.  Well, for each staically allocated variable that gets put in the RAM segment, there is another
variable of the same size allocated in FLASH, with the initial values of these variables inside there.
This segment is copied to RAM by the startup code and now we have statically allocated variables
that are initialized.

ANSI C and other modern standards specify that uninitialized variables implicitly get set to zero.  There
will be another segment that isn't initialized from FLASH.  This segment does, however, need to get
cleared to zeros.  The startup code does this as well.

These are the most important steps (and there are others in the startup code) that run before main() starts
running.  When the startup code is done, its last operation is to jump to the address where main() starts.

There are some curious things about main you could ask, like "what about argc and argv?"  "What happens
when main exits?"  These depend on the compiler implementation and really are oddball questions that don't
need to get answered here.  Generally in embedded programming you may or may not get something
in argc/argv, or you may not.  main() does not generally exit.  I do know that if you exit from main()
in the Win-AVR compiler, it will jump to the reset location and effectively restart the processor from the
beginning.

How about that static int z=7 in main()?  The static keyword tells the compiler to allocate the
variable as if it were file scope (like the variable x), but it won't be globally visible now because it's hidden
in a function.  The variable has an eternal life, but can't be accessed by just any function.  This also has the
effect of creating a variable that won't change from function call to function call.

This also can have an effect on re-entrancy:  if a function is called, and an interrupt arrives and a second
copy of the function starts running, what will happen?  Both will see the static variable and could produce
undesired side effects.  Keep this in mind if you are writing an interrupt handler, and remember that declaring
a variable file-scope generally won't make this problem go away.

static variables are somewhat rare.  They have their place, but there are generally better options.

Now for that char *str.  Remember, in C "this is a string" places the string in a ROM location,
the location of which is a char *.  It then provides a pointer to this string as the value.  You can change what
str points to by reassigning it, but the "this is a string" will remain in FLASH, just like it was.  The
pointer to it is inaccessible, so if you reassign str, you will lose the location.  So, copy str elsewhere if you
need it later.