Storage durations of objects

Language C makes a sharp division between ‘Objects’ and ‘Values’. ‘Values’ are the subject matter of any program, the values only created and destroyed during execution environment but in execution environment the commands are executed line by line and thus before moving to next step or next line step, it destroys values automatically. Thus, we can say that all ‘Values’ are short-lived and thus they are destroyed during the process of program but we want to use these values later in the program. To use the ‘values’ at later stage we store them into ‘Objects’. ‘Objects’ are often found via ‘Identifiers’. Confusing, a little bit but let us clear it by example:
#include
int main(void){
    int p, s,t;
    p=25;
    s=10;
    t=25+10;
    return 0;
}
Now, in the above example p, s, t are three Objects and during the execution of the program. We want to have the value 25, as told you earlier that values are short-lived and they only created in execution environment. Values are short lived and thus the program moving to next statement automatically destroyed it but we need it thus we store the value of 25 in Object p.
It is now clear that we store values in Object and we need to know the storage duration of Object. Objects generally have a particular type (such as int or double), set at the time the object is created, and fixed for the lifetime of that object. On the other hand, objects always have a specific lifetime, also called a storage duration. Whenever you declare an ordinary variable, this creates an object.

Linkage Vs Storage Duration

Linkage is the property of Identifier and storage duration is property of Objects. The confusion is created by declarations are done through same set of Keywords. The Storage Duration of the Object can be automatic, static, allocated and thread, Linkage of an Identifier can be Internal, External or None. Thus it is correct to say that Objects are referred by its name. Important Point to Remember:
  • Objects have storage duration not linkage and Identifier’s have Linkage not Storage Duration.
  • Identifiers are names you choose to describe your variables/Object and so on. A variable/Object is referenced to by an identifier, and denotes a memory area that can be manipulated through the use of the identifier.

Definition

6.2.4(2) defines the storage duration as:

The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reach the end of its lifetime.

A declaration is a sequence of declaration specifier’s, followed by a comma separated sequence of declarator’s. Whenever an Identifier is declared some storage space depending upon the type of identifier is reserved by the compiler. For instance, the declaration statement int a=20;, the compiler reserves 2 bytes of storage space. Upon execution of program, the reserved storage space is allocated, this allocated space is denoted by Objects. The duration for which storage space is reserved depends upon the storage duration of object. The storage duration of an Object determines its lifetime. Thus, the lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it.
At first glance the phrase constant appears to prevent a C implementation from using a garbage collector that moves objects in storage. But what is an address? An implementation could choose to represent object addresses as an index into an area of pre-allocated storage. This indexed element holds the real address in memory of the object’s representation bits. The details of this extra indirection operation is dealt with by the translator. A garbage collector would only need to update the indexed elements after storage had been compacted, and the program would know nothing about what had happened. The last-stored value may have occurred as a result of an assignment operator, external factors for an object declared with the volatile storage-class, or another operator that updates the value held in an object. For example:
#include <stdio.h>
int add_two(int x, int y){            /* the add_two function */
    static int counter = 1;
    printf("This is the function call of %d ", counter++);
    return (x + y);
}
int main(void){                       /* the main function */
    int i, j;
    for (i=0, j=5; i<5; i++, j--){
        printf("the addition of %d and %d is %d.\n", i, j, add_two(i, j));
    }
    return 0;
}

Output:

This is the function call of 1 the addition of 0 and 5 is 5.
This is the function call of 2 the addition of 1 and 4 is 5.
This is the function call of 1 the addition of 2 and 3 is 5.
This is the function call of 1 the addition of 3 and 2 is 5.
This is the function call of 1 the addition of 4 and 1 is 5.
Lifetime of the storage is associated with the identified object; the type determines the meaning of the values found in the identified object. A name also has a scope, which is the region of the program in which it is known, and a linkage, which determines whether the same name in another scope refers to the same object or function. When the lifetime of an object ends, nothing usually happens to the last-stored value held at that location. Programs that access the storage location that held the object, soon after the object’s lifetime has ended, often work as expected.   It is not the object pointed at, but the value of pointers pointing at it that become indeterminate. Once its value becomes indeterminate, the value of the pointer cannot even be read; for instance, compared for equality with another pointer. An object having a pointer type has an indeterminate value at the start of its lifetime, like any other object.
According to 3.19.1 indeterminate value

either an unspecified value or a trap representation

This is the value objects have prior to being assigned one by an executing program. In practice it is a conceptual value because, in most implementations, an object’s value representation makes use of all bit patterns available in its object representation. Accessing an object that has an unspecified value results in unspecified behavior. However, accessing an object having a trap representation can result in undefined behavior.

Static Storage Duration

According to 6.2.4 – Paragraph 3, static storage duration means:

An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.

Static storage thus refers to memory locations that persist for the life of the program. Objects have storage duration, identifiers have linkage. The visibility of an identifier defined with internal linkage may be limited to the block that defined it. Global objects and local static objects in functions resides as static storage duration. An object with static storage duration resides in the same memory address throughout the program’s execution. Every such object is constructed only once during the lifetime of the program. The scope of an object declared static in a function is restricted to that function.
The total number of bytes of storage required for static storage duration objects is usually written into the program image in translation phase 8. During program startup this amount of storage is requested from the host environment. Objects and literal’s that have static storage duration are usually placed in a fixed area of memory, which is reserved on program startup. This is possible because the amount of storage needed is known prior to program execution and does not change during execution.
In C – There are essentially three uses of the word ‘static’. It is important to note here that the usage is slightly different from C++, due to the non-existence of classes. The threes instances of use are:
  1. A variable declared as static inside a function.
  2. A variable declared as static at the file scope level, accessible to all functions in that file.
  3. A function defined as ‘static’ within a .c file.

First Case

The first one is most commonly used in projects and is fairly well understood. It relates to a variable retaining its value between function invocations. i.e if you have:
void foo(){
   static int test_var = 0;
   test_var+=1;
   printf("Value of Static variable is %d", testingStaticVariable);
}
void main(){
    for(int i = 0; i < 3; i++){
    foo(); // Called three times.
    }
}
For the above mentioned sample program, you will find that the testingStaticVariable will retain its value every time foo() is called. Therefore the output would look like this:
Value of Static variable is 1
Value of Static variable is 2
Value of Static variable is 3
Therefore you can see that by defining the variable as static it retained it’s value between the multiple invocations of foo.

Second Case

Global variables are to be defined globally, e.g. at the beginning of the file (of course outside all the functions in the file) or in a header file. Global variables retain value throughout the life of the program. A global variable can be accessed from any file with an external linkage. So the scope of a global variable is throughout the program. A global variable is allocated from the data section of the memory. Global static variable means a variable declared above all the functions(even main) with the static keyword, this will tell that this variable is private to the file where it is present, means we can access this variable within this same file, it is not visible in another file. Global Static Variable is going to have internal linkage. Let us go through example:
test1.c
#include<stdio.h>
#include "header.h"
int i = 100;
int main() {
    printf("This is the scope of the global variable:%d\n",i);
    foo();
    foo();
    printf("This is not a scope of static variable:%d\n",j);
    return 0;
}
test2.c
extern int i;
static int j = 200;
int foo() {
    printf("This is the scope of the global variable: %d\n",i);
    j++;
    printf("in func foo() the scope of static j is: %d\n",j);
    return 0;
}
header.h
extern int foo();
extern int j;
When we try to compile the compiler would show error as /tmp/cckXvIiw.o(.text+0x2f): In function ‘main’ undefined reference to ‘j’. This is because we are trying to access the global static variable ‘j’ which is only limited to text2.c file functions only. For understand this situation more one has read translation unit in detail.

Third Case

This is probably the least used, but is extremely useful, if one knows how to utilize this feature. There are no access specifiers for the C programming language. i.e there are no functions you can declare private or public. However, you can almost achieve the same level of protection, by declaring functions as static. If you declare a function as static within a .c file, only other functions within that .c file can see/access this function, whereas the interface or the header file .h will not contain these function prototypes, and therefore, any files within the project that might include the header file, will still not have access to the ‘private’ (by declaring them as static) functions defined in the .c file.This achieves some level of protection or abstraction, that is really useful in large projects, where you want to be able to provide an interface (via the header file) to this .c file or more appropriately referred to as a compilation unit, but do not want to expose other functions defined within this compilation unit. So, essentially you are localizing the scope of these functions to a particular compilation unit, such that even though other modules might have access to this module, they will be unable to see these functions. Typically the way to achieve this is, One would have, an interface file (visible to everyone in the project) called example.h a source file that contains the implementation of the interface, called example.c
example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
void doSomethingImp();

#endif
example.c
uint8_t exampleVariable = 0;            // global variable definition
/******************
* local function prototypes declared here as
* static, not visible outside example.c
*******************/
static void setVariable(uInt8_t num);
static uint8_ t geVariable();

void doSomethingImp(){             //let's do something here with a variable.
    uint8_t foo = 0;
    setVariable(5);
    foo = getVariable();
}

static void setVariable(uInt8_t num){
    exampleVariable = num;
}

static uint8_ t geVariable(){
    return exampleVariable;
}
Now in the above mentioned sample project, if another foo.c included example.h, it would only have access to the doSomethingImp() function. It WOULD NOT HAVE access to the setVariable(uInt8_t num) and the geVariable() functions since they were declared as static, which localised their scope to the example.c compilation unit.

Automatic Storage Duration

According to 6.2.4 – Paragraph 5, static storage duration means:

An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration, as do some compound literals. The result of attempting to indirectly access an object with automatic storage duration from a thread other than the one with which the object is associated is implementation defined.

An Automatic Objects or Automatic variable is “born” when it is defined and scope for it is allocated, and is automatically ceases to exit at the end of the block containing the definition of the variable. This will be at the closing bracket matching the first opening bracket that precedes the declaration of the variable.  They are destroyed and their storage is reclaimed automatically when the function or block in which they are declared exits. Thus, on each entry into a function or a block, a fresh set of its automatic objects is created. The default value of automatic variables and non-class objects is indeterminate. Automatic objects have no linkage; you cannot refer to them from a different translation unit and their visibility is limited to the scope in which they are declared. Note: Variables with file scope are automatically static variables.
These objects occur in block scope and are commonly known as local variables. The storage-class specifier auto can also be used in the definition of such objects. Apart from translator test programs, this keyword is rarely used, although some developers use it as a source layout aid.  By default, every variable pertains to auto storage class.  The terminology automatic storage duration is rarely used by developers. Common practice is to use the term block scope to refer to such objects. The special case of block scope objects having static storage duration is called out in the infrequent cases it occurs.
/* Function to find the greatest common divisor
of two non-negative integer values */
#include <stdio.h>
void gcd (int u, int v){
    int temp;
    printf ("The gcd of %i and %i is ", u, v);
    while ( v != 0 ) {
        temp = u % v;
        u = v;
        v = temp;
    }
    printf ("%i\n", u);
}

int main (void){
    gcd (150, 35);
    gcd (1026, 405);
    gcd (83, 240);
    return 0;
}

Output

The gcd of 150 and 35 is 5
The gcd of 1026 and 405 is 27
The gcd of 83 and 240 is 1
The function gcd is defined to take two integer arguments.The function refers to these arguments through their formal parameter names u and v. After declaring the variable temp to be of type int, the program displays the values of the arguments u and v, together with an appropriate message at the terminal.The function then calculates and displays the greatest common divisor of the two integers.
You might be wondering why there are two printf statements inside the function gcd. You must display the values of u and v before you enter the while loop because their values are changed inside the loop. If you wait until after the loop has finished, the values displayed for u and v do not at all resemble the original values that were passed to the routine. Another solution to this problem is to assign the values of u and v to two variables before entering the while loop.The values of these two variables can then be displayed together with the value of u (the greatest common divisor) using a single printf statement after the while loop has completed.
// Program to illustrate static and automatic variables
#include <stdio.h>
void auto_static (void){
    int autoVar = 1;
    static int staticVar = 1;
    printf ("automatic = %i, static = %i\n", autoVar, staticVar);
    ++autoVar;
    ++staticVar;
}
int main (void){
    int i;
    void auto_static (void);
    for ( i = 0; i < 5; ++i ){
        auto_static ();
    }
    return 0;
}

Output

automatic = 1, static = 1
automatic = 1, static = 2
automatic = 1, static = 3
automatic = 1, static = 4
automatic = 1, static = 5
Inside the auto_static function, two local variables are declared.The first variable, called autoVar, is an automatic variable of type int with an initial value of 1. The second variable, called staticVar, is a static variable, also of type int and also with an initial value of 1. The function calls the printf routine to display the values of these two variables. After this, the variables are each incremented by 1, and execution of the function is then complete.
The main routine sets up a loop to call the auto_static function five times.The output from program points out the difference between the two variable types. The value of the automatic variable is listed as 1 for each line of the display. This is because its value is set to 1 each time the function is called. On the other hand, the output shows the value of the static variable steadily increasing from 1 through 5. This is because its value is set equal to 1 only once – when program execution  – and because its value is retained from one function call to the next.
The choice of whether to use a static variable or automatic variable depends upon the intended use of the variable. If you want the variable to retain its value from one function call to the next, use a static variable. Also, if your function uses a variable whose value is set once and then never changes, you might want to declare the variable static, as it saves the inefficiency of having the variable re-initialized each time that the function is called.This efficiency consideration is even more important when dealing with arrays. From the other direction, if the value of a local variable must be initialized at the beginning of each function call, an automatic variable seems the logical choice.
Advertisements
Tagged , , , , , ,

3 thoughts on “Storage durations of objects

  1. I am in fact pleased to read this website posts which includes lots of valuable data, thanks for providing these information.

  2. Henri says:

    excellent points altogether, you just won a new reader. What could you suggest about your put up that you just made some days ago? Any certain?

  3. Gert says:

    You have mentioned very interesting points! ps nice web site.

Comments are closed.

%d bloggers like this: