Storage Classes in C Programming Language

Storage Classes in C Programming Language  

  C Programming Language    

trobo c
troboC


Storage class specifiers are the keywords which can appear next to the top-level type of a declaration. The use of these keywords affects the storage duration and linkage of the declared object, depending on whether it is declared at file scope or at block scope:

1. auto

This storage class denotes that an identifier has automatic storage duration. This means once the scope in which the identifier was defined ends, the object denoted by the identifier is no longer valid.
Since all objects, not living in global scope or being declared static, have automatic storage duration by default when defined, this keyword is mostly of historical interest and should not be used:
int foo(void)
{
    /* An integer with automatic storage duration. */
    auto int i = 3;

    /* Same */
    int j = 5;

    return 0;
} /* The values of i and j are no longer able to be used. */

2. register

Hints to the compiler that access to an object should be as fast as possible. The register storage class is more appropriate for variables that are defined inside a block and are accessed with high frequency. For example,
/* prints the sum of the first 5 integers*/
/* code assumed to be part of a function body*/ 
{ 
    register int k, sum;
    for(k = 1, sum = 0; k < 6; sum += k, k++);
        printf("\t%d\n",sum);
}
In C11, The _Alignof operator is also allowed to be used with register arrays.

3. extern

Used to declare an object or function that is defined elsewhere (and that has external linkage). In general, it is used to declare an object or function to be used in a module that is not the one in which the corresponding object or function is defined:
/* file1.c */
int foo = 2;  /* Has external linkage since it is declared at file scope. */
/* file2.c */
#include 
int main(void)
{
    /* `extern` keyword refers to external definition of `foo`. */
    extern int foo;
    printf("%d\n", foo);
    return 0;
}
Things get slightly more interesting with the introduction of the inline keyword in C99:
Hints to the compiler that the function bar might be inlined and suppresses the generation of an external symbol, unless stated otherwise.
/* Should usually be place in a header file such that all users see the definition */
inline void bar(int drink)
{
    printf("You ordered drink no.%d\n", drink);
}
To be found in just one .c file. Creates an external function definition of bar for use by other files. The compiler is allowed to choose between the inline version and the external definition when bar is called. Without this line, bar would only be an inline function, and other files would not be able to call it.
extern void bar(int);

4. static

The static storage class serves different purposes, depending on the location of the declaration in the file:
To confine the identifier to that translation unit only (scope=file).
/* No other translation unit can use this variable. */
static int i;

/* Same; static is attached to the function type of f, not the return type int. */
static int f(int n);
To save data for use with the next call of a function (scope=block):
 void foo() { 
     static int a = 0; /* has static storage duration and its lifetime is the
                        * entire execution of the program; initialized to 0 on 
                        * first function call */ 
     int b = 0; /* b has block scope and has automatic storage duration and 
                 * only "exists" within function */
     
     a += 10;
     b += 10; 

     printf("static int a = %d, int b = %d\n", a, b);
 }

 int main(void) {
     int i;
     for (i = 0; i < 5; i++) {
         foo();
     }

     return 0;
 }
/*
This code prints:

 static int a = 10, int b = 10
 static int a = 20, int b = 10
 static int a = 30, int b = 10
 static int a = 40, int b = 10
 static int a = 50, int b = 10
*/

5. ___Thread_local   


   
turboC language
turbo C

This was a new storage specifier introduced in C11 along with multi-threading. This isn't available in earlier C standards.
Denotes thread storage duration. A variable declared with _Thread_local storage specifier denotes that the object is local to that thread and its lifetime is the entire execution of the thread in which it's created. It can also appear along with static or extern.
#include 
#include 
#define SIZE 5

int thread_func(void *id)
{
    /* thread local variable i. */
    static _Thread_local int i;

    /* Prints the ID passed from main() and the address of the i.
     * Running this program will print different addresses for i, showing
     * that they are all distinct objects. */
    printf("From thread:[%d], Address of i (thread local): %p\n", *(int*)id, (void*)&i);

    return 0;
}

int main(void)
{
    thrd_t id[SIZE];
    int arr[SIZE] = {1, 2, 3, 4, 5};

    /* create 5 threads. */
    for(int i = 0; i < SIZE; i++) {
        thrd_create(&id[i], thread_func, &arr[i]);
    }

    /* wait for threads to complete. */
    for(int i = 0; i < SIZE; i++) {
        thrd_join(id[i], NULL);
    }
}

6. typedef

Defines a new type based on an existing type. Its syntax mirrors that of a variable declaration.
/* Byte can be used wherever `unsigned char` is needed */
typedef unsigned char Byte;

/* Integer is the type used to declare an array consisting of a single int */
typedef int Integer[1];

/* NodeRef is a type used for pointers to a structure type with the tag "node" */
typedef struct node *NodeRef;

/* SigHandler is the function pointer type that gets passed to the signal function. */
typedef void (*SigHandler)(int);
While not technically a storage class, a compiler will treat it as one since none of the other storage classes are allowed if the typedef keyword is used.
The typedefs are important and should not be substituted with #define macro.
typedef int newType; 
newType *ptr;        // ptr is pointer to variable of type 'newType' aka int
However,
#define int newType
newType *ptr;        // Even though macros are exact replacements to words,
 
 this doesn't result to a pointer to variable of type 'newType' aka int

   

Preprocessor Directives - C Programming 

 

The C preprocessor is a macro processor that is used automatically by the C compiler to transform your program before actual compilation (Proprocessor direcives are executed before compilation.). It is called a macro processor because it allows you to define macros, which are brief abbreviations for longer constructs. A macro is a segment of code which is replaced by the value of macro. Macro is defined by #define directive.
Preprocessing directives are lines in your program that start with #. The # is followed by an identifier that is the directive name. For example, #define is the directive that defines a macro. Whitespace is also allowed before and after the #.
The # and the directive name cannot come from a macro expansion. For example, if foo is defined as a macro expanding to define, that does not make #foo a valid preprocessing directive.
All preprocessor directives starts with hash # symbol.

List of preprocessor directives :

  1. #include
  2. #define
  3. #undef
  4. #ifdef
  5. #ifndef
  6. #if
  7. #else
  8. #elif
  9. #endif
  10. #error
  11. #pragma

1. #include

The #include preprocessor directive is used to paste code of given file into current file. It is used include system-defined and user-defined header files. If included file is not found, compiler renders error. It has three variants:

#include

This variant is used for system header files. It searches for a file named file in a list of directories specified by you, then in a standard list of system directories.

#include "file"

This variant is used for header files of your own program. It searches for a file named file first in the current directory, then in the same directories used for system header files. The current directory is the directory of the current input file.

#include anything else

This variant is called a computed #include. Any #include directive whose argument does not fit the above two forms is a computed include.

2. Macro's (#define)

Let's start with macro, as we discuss, a macro is a segment of code which is replaced by the value of macro. Macro is defined by #define directive.
Syntax
#define token value  
There are two types of macros:
  1. Object-like Macros
  2. Function-like Macros

1. Object-like Macros

The object-like macro is an identifier that is replaced by value. It is widely used to represent numeric constants. For example:
#define PI 3.1415  
Here, PI is the macro name which will be replaced by the value 3.14. Let's see an example of Object-like Macros :
#include   
#define PI 3.1415 
main() 
{  
   printf("%f",PI);  
}  
Output:
3.14000

2. Function-like Macros

The function-like macro looks like function call. For example:
#define MIN(a,b) ((a)<(b)?(a):(b))    
Here, MIN is the macro name. Let's see an example of Function-like Macros :
#include   
#define MIN(a,b) ((a)<(b)?(a):(b))  
void main() {  
   printf("Minimum between 10 and 20 is: %d\n", MIN(10,20));    
}  
Output:
Minimum between 10 and 20 is: 10

Preprocessor Formatting

A preprocessing directive cannot be more than one line in normal circumstances. It may be split cosmetically with Backslash-Newline. Comments containing Newlines can also divide the directive into multiple lines.
for example, you can split a line cosmetically with Backslash-Newline anywhere:
/*
*/ # /*
*/ defi\
ne FO\
O 10\
20
is equivalent into #define FOO 1020.

3. #undef

To undefine a macro means to cancel its definition. This is done with the #undef directive.
Syntax:
#undef token  
define and undefine example
#include   
#define PI 3.1415  
#undef PI  
main() {  
   printf("%f",PI);  
}  
Output:
Compile Time Error: 'PI' undeclared

4. #ifdef

The #ifdef preprocessor directive checks if macro is defined by #define. If yes, it executes the code.
Syntax:
#ifdef MACRO  
//code  
#endif

5. #ifndef

The #ifndef preprocessor directive checks if macro is not defined by #define. If yes, it executes the code.
Syntax:
#ifndef MACRO  
//code  
#endif  

6. #if

The #if preprocessor directive evaluates the expression or condition. If condition is true, it executes the code.
Syntax:
#if expression  
//code  
#endif  

7. #else

The #else preprocessor directive evaluates the expression or condition if condition of #if is false. It can be used with #if, #elif, #ifdef and #ifndef directives.
Syntax:
#if expression  
//if code  
#else  
//else code  
#endif
Syntax with #elif
#if expression  
//if code  
#elif expression  
//elif code  
#else  
//else code  
#endif  
Example
#include   
#include   
#define NUMBER 1  
void main() {  
#if NUMBER==0  
printf("Value of Number is: %d",NUMBER);  
#else  
print("Value of Number is non-zero");  
#endif         
getch();  
}  
Output
Value of Number is non-zero

8. #error

The #error preprocessor directive indicates error. The compiler gives fatal error if #error directive is found and skips further compilation process.
C #error example
#include  
#ifndef __MATH_H  
#error First include then compile  
#else  
void main(){  
    float a;  
    a=sqrt(7);  
    printf("%f",a);  
}  
#endif

9. #pragma

The #pragma preprocessor directive is used to provide additional information to the compiler. The #pragma directive is used by the compiler to offer machine or operating-system feature. Different compilers can provide different usage of #pragma directive.
Syntax:
#pragma token 
Example
#include  
#include  
  
void func() ;  
  
#pragma startup func  
#pragma exit func  
  
void main(){  
printf("\nI am in main");  
getch();  
}  
  
void func(){  
printf("\nI am in func");  
getch();  
}  
Output
I am in func
I am in main
I am in fun


Comments