Global and static variables


#1

For those who are new to programming, here is an introduction to global and static variables.

To get the most out of this, you should create a Foundation tool project to create a program from the files shown below and you should experiment with the qualifiers extern and static.

The term global pertains to the visibility of a variable throughout a program. If a variable is global, it can be accessed anywhere in the program. Also, unlike local variables, a global variable is kept alive as long as the program is running. A global variable must be unique; that is, there must exist only one definition for it in the same program. It’s illegal to define two global variables using the same name in the same program.

A global variable must be defined outside a function, anywhere in the file, but usually before it’s referenced in the same file or before the definitions of functions start.

In what follows when you see the declaration extern double myPI, read it as: the variable myPI is defined as a double somewhere else (usually in some other module) and will be accessed here (where it appears) as a global variable.

Consider a program consisting of four modules, in files: main.m, Foo.m, Bar.m, and FooBar.m.

//  main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    // Access the global myPI
    extern double myPI;
  
    // Set PI to a more accurate value
    myPI = 4 * atan (1.0);
}
//  Foo.m

void Foo ()
{
    // Access the global myPI
    extern double myPI;
}
//  Bar.m

void Bar ()
{
    // Access the global myPI
    extern double myPI;
}
//  FooBar.m

// myPI is  a global variable throughout the program
double myPI = 22.0/7.0;

void FooBar ()
{
}

Here myPI is a global variable defined in FooBar.m and is thus accessible from anywhere in the program with the extern declaration: extern double myPI.

To understand what’s really going on:

  1. Create a foundation project in Xcode and add the files shown above.
  2. Compile and run.

Since there are no log statements, you won’t be able to see anything in the console view. But be assured that main.m and Foo.m and Bar.m are accessing the same global variable, myPI, defined in FooBar.m.

However, this is what you should do next: Go and edit FooBar.m and put static in front of double myPI, making it static double myPI. Now if you compile the project, you should get linker errors:

Undefined symbols for architecture x86_64:
  "_myPI", referenced from:
      _main in main.o
      _Foo in Foo.o
      _Bar in Bar.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The reason for the linker errors: making the global variable myPI static downgraded its visibility from being truly global to being visible only in the module FooBar. When the linker is looking for it, because there is a demand for it in main.m and Foo.m and Bar.m, the linker can’t find the variable’s definition because it is not visible from outside the module FooBar.m where it is defined.

Next, you should go and remove the extern keyword from extern double myPI to make myPI a local variable in main.m, Foo.b, and Bar.m. Now you can compile and run, but you won’t be able to see anything in the console view.

====================================================================================================

Here is the verbose version of the above files, with log statements:

//  main.m

#import <Foundation/Foundation.h>

#define MY_Log1(P1, P2) NSLog (@"%s: "P1, __PRETTY_FUNCTION__, P2)

void Foo ();
void Bar ();
void FooBar ();

int main (int argc, const char * argv[])
{
    // Access the global myPI
    extern double myPI;
    MY_Log1 (@"myPI = %f", myPI);
    Foo ();
    Bar ();
    FooBar ();
   
    // Set PI to a more accurate value
    myPI = 4 * atan (1.0);
    MY_Log1 ("Changed myPI to: %f", myPI);
    Foo ();
    Bar ();
    FooBar ();
    return 0;
}
//  Foo.m

#define MY_Log1(P1, P2) NSLog (@"%s: "P1, __PRETTY_FUNCTION__, P2)

void Foo ()
{
    // Access the global myPI
    extern double myPI;
    MY_Log1 (@"myPI = %f", myPI);    
}
//  Bar.m

#define MY_Log1(P1, P2) NSLog (@"%s: "P1, __PRETTY_FUNCTION__, P2)

void Bar ()
{
    // Access the global myPI
    extern double myPI;
    MY_Log1 (@"myPI = %f", myPI);    
}
//  FooBar.m

// myPI is  a global variable throughout the program
double myPI = 22.0/7.0;

#define MY_Log1(P1, P2) NSLog (@"%s: "P1, __PRETTY_FUNCTION__, P2)

void FooBar ()
{
    MY_Log1 (@"myPI = %f", myPI);    
}

If you build and run you should see the following output:

2012-11-16 13:23:47.006 Global[17405:403] int main(int, const char **): myPI = 3.142857
2012-11-16 13:23:47.008 Global[17405:403] void Foo(): myPI = 3.142857
2012-11-16 13:23:47.008 Global[17405:403] void Bar(): myPI = 3.142857
2012-11-16 13:23:47.009 Global[17405:403] void FooBar(): myPI = 3.142857
2012-11-16 13:23:47.009 Global[17405:403] int main(int, const char **): Changed myPI to: 3.141593
2012-11-16 13:23:47.010 Global[17405:403] void Foo(): myPI = 3.141593
2012-11-16 13:23:47.010 Global[17405:403] void Bar(): myPI = 3.141593
2012-11-16 13:23:47.010 Global[17405:403] void FooBar(): myPI = 3.141593

Notice how the module main.m can change the value of myPI and the change is reflected in other modules.

There is also a second kind of global variable, of which the scope is restricted to the file in which it appears. We call this a static global variable:

// Reader.m

// These global variables, since they are static,  are not accessible from other modules
// They are accessible only in this module
//
static unsigned long charCount;
static unsigned long lineCount;
...
int Read (FILE *file)
{
     ...
     charCount += 1;
     ...
     lineCount += 1;
     ...
}
...
void PrintCounts (const char *prefix)
{
     NSLog (@"%s: %ld characters %ld lines", prefix, charCount, lineCount);
}

====================================================================================================

A static variable can also be defined inside a function:

void Start ()
{
   static BOOL started = NO;

   if (started)
        return;
   ...
   started = YES;
}

A static variable defined inside a function also lasts as long as the program is running, but it’s accessible only in the function it is defined.

Finally, without due thought, don’t sprinkle your code with static or global variables even if it seems cool to do so; avoid using them whenever you can.


#2

Thank you for the great pointers Ibex10.

Im having a little trouble with some of your code

[code]double myPI = 22.0/7.0;

#define MY_Log1(P1, P2) NSLog (@"%s: "P1, PRETTY_FUNCTION, P2)

void FooBar ()
{
MY_Log1 (@“myPI = %f”, myPI);
}[/code]

I understand how your using the #define directive to create the macro, how ever I’m a bit puzzled by its output, and I don’t understand the purpose of “PRETTY_FUNCTION”.

How is it that the function calling MY_Log1 gets printed out?

as in: “void FooBar(): myPI = 3.141593”, How does “void FooBar():” get printed out?

Why is there no comma separating the string @"%s: " from P1 ?

What is “PRETTY_FUNCTION” ? Tried option clicking on this but nothing comes up. I also tried altering it a bit, but then get an compiler error.