Broad Network


Scopes of Identifier in the C Computer Language

Part 34 of the Complete C Course

Foreword: Scopes of Identifier in the C Computer Language

By: Chrysanthus Date Published: 22 Jun 2024

Introduction

An identifier can denote an object, a function, a struct, a union, an enumeration, a struct or union member, etc. Remember, a tag is the type (class) name of a struct, union or  enumeration. The same identifier can denote different entities at different points in the program. For each different entity that an identifier designates, the identifier is visible (i.e., can be used) only within a region of program text, called its scope. Different entities designated by the same identifier (same name) either have different scopes, or are in different name spaces (see later). There are four kinds of scopes: block, function, function prototype and file.

Block Scope
Long statements are the if, while, do, for, and switch statements. Each of these statements can have a block. Consider the following program:

    int main(int argc, char *argv[])
    {
        if (1 == 1) {
            /*some statements*/
            
            int inte = 25;
            
            /*some statements*/
            
            printf("%i\n", inte);
        }
        
        //printf("%i\n", inte);

        return 0;
    }

The output is 25. The integer, inte can be seen from its point of declaration, to the end of the if-block. The output of 25 was printed by the printf() function call, inside the if-block. That is block scope. inte cannot be seen outside the if-block. If the commenting double forward slash in front of the printf statement below the if-block is taken off, an error message will be outputted at compile time, and the program will not compile. When a program is not compiled into the executable form, it cannot run (cannot be executed). The reader should test the program, with and without the double forward slash of the outer printf statement.

Block Scope and for-loop
Consider the following for-loop:

        for (int i=0; i<5; i++) {
            /*some statements*/
            
            printf("%i ", i);
            
            /*some statements*/
        }
        printf("\n");

The for-loop statement begins with the reserved word "for", followed by parentheses and the block delimited by curly brackets. The important information is this: the scope of the variable, i begins from the point of declaration in the parentheses, at "int i=0" , and ends at the end of the block. In other words, the variable, i can be seen in the parentheses, from its point of declaration, to the end of the parentheses, and continue to be seen anywhere inside the block {}. Note, it cannot be seen outside the block.

Nested Blocks

In the following program, there are three blocks:

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
        /* A statements*/
        if (1 == 1) {
            /* B statements*/
            
            if (2 == 2) {
                /* C statements*/
            }
            
            /* B statements*/
        }
        /* A statements*/

        return 0;
    }

There is the block of the C main() function; there is the nested if-block beginning with (1 == 1) in the block of the main() function; and there is the innermost nested if-block beginning with if (2 == 2), in the block of the outrer if-block beginning with (1 == 1).

The important information is this:  a variable declared inside a block, cannot be seen outside the block. This means that a variable declared inside a nested block, cannot be seen outside, and in its nesting block. Also, the scope of the nesting block, is before the nested block and after the nested block. Look at where the A statements are, where the B statements are, and where the C statements are, in the above program.

Another important information is this: A variable declared in a block, is seen inside a nested block and inside a nested nested block, and in all inner nested blocks. The following program illustrates this:

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
        int inte = 5;
        if (1 == 1) {
            printf("%i\n", inte);
            if (2 == 2) {
                printf("%i\n", inte);
            }
        }
        printf("%i\n", inte);

        return 0;
    }

The output is 5, 5, 5 in three separate lines.

Overriding in Nested Block

Now, since a variable declared in a nested block cannot be seen in the nesting block, but a variable declared in a nesting block can be seen in a nested block, it means that a variable declared in a nesting block can be re-declared in a nested block, for a different object, ending up with two independent objects: one in the inner block and the other in the outer block. Both objects have the same name. The following program illustrates this:

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
        int inte = 5;
        if (1 == 1) {
            int inte = 6;
            
            printf("%i\n", inte);
            if (2 == 2) {
                printf("%i\n", inte);
            }
        }
        printf("%i\n", inte);

        return 0;
    }

The integer inte, is declared outside the block beginning with (1 == 1) as "int inte = 5;". It is re-declared inside the block to have a different value as, "int inte = 6;". Actually, this is not re-declaration. It is two declarations in two different scopes with the same name (identifier). And so the output is 6, 6, 5 in three different lines. And so the variable has been overridden completely inside the nested block.

Of course the overriding can be with a different type, as in the following program:

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
        int inte = 5;
        if (1 == 1) {
            float inte = 6.6;
            
            printf("%f\n", inte);
            if (2 == 2) {
                printf("%f\n", inte);
            }
        }
        printf("%i\n", inte);

        return 0;
    }

The output is 6.600000, 6.600000, 5, with each value on a separate line.

Function Scope

Consider the following program:

    #include <stdio.h>
    
    int fun (int a, int b, int c) {
        printf("%i\n", a);
        printf("%i\n", b);
        int d = 3;
        printf("%i\n", d);
        return c;
    }
    
    int main(int argc, char *argv[])
    {
        int myInt = fun(1, 2, 4);
        printf("%i\n", myInt);

        return 0;
    }

The output is 1, 2, 3, 4, with each value on a separate line. The signature of the function is,

    int fun (int a, int b, int c)

The function call is: "fun(1, 2, 3)". When the function is called, the value of a becomes 1, and a is seen from its point of declaration in the parentheses, to the end of the parentheses, ), and is seen all over in the function block. b is seen from its point of declaration in the parentheses, to the end of the parentheses, and is seen all over in the function block. c is seen from its point of declaration in the parentheses, to the end of the parentheses, and is seen all over in the function block. a, b, and c are in the function scope of the function, fun(). A scope is a region of text, where a variable is seen.

The variable, d is in the block scope of the function, fun(), as well as it is in the function scope of the same function; though d is not seen in the parentheses. The function scope begins from within the parentheses to the end of its block.

Function Prototype Scope

The prototype of a function, is its signature ending with a semicolon, without the function body (block). The prototype of a function and the function definition, can be in the same file, as the following program shows:

    #include <stdio.h>
    
    int fun (int a, int b, int c);
    
    int fun (int a, int b, int c) {
        printf("%i\n", a);
        printf("%i\n", b);
        int d = 3;
        printf("%i\n", d);
        return c;
    }
    
    int main(int argc, char *argv[])
    {
        int myInt = fun(1, 2, 4);
        printf("%i\n", myInt);

        return 0;
    }

This program is the same as the previous program, except that the function, fun() has the prototype, "int fun (int a, int b, int c);" separately. The function signature is always attached to the function body in the function definition. The output is the same as for the previous program.

In the prototype, the scope of the each of the variables, begin from its point of declaration, to the end of the right brackets, ). It does not, in theory, continue into the block of the function, since the block of the function is detached from the prototype. That is function prototype scope.

Label Name and Function Scope
The label name (identifier) has function scope. The following program illustrates this with the C main() function:

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
        printf("%c ", 'A');
        goto labl1;
        printf("%c ", 'B');
        labl1:
        printf("%c ", 'C');
        goto labl2;
        printf("%c ", 'D');
        labl2:
        printf("\n");

        return 0;
    }

The output is A C.

File Scope
If an identifier (variable) is said to have a file scope, it means that the identifier can be seen everywhere in the file. The following program illustrates this:

    #include <stdio.h>
    
    int inte = 5;
    
    void fn() {
        printf("%i\n", inte);
    }
    
    int main(int argc, char *argv[])
    {
        fn();
        
        if (1 == 1) {
        
            printf("%i\n", inte);
            
            if (2 == 2) {
                printf("%i\n", inte);
            }
        }
        
        printf("%i\n", inte);

        return 0;
    }

The output is 5, 5, 5, 5 with each value on a separate line. For an identifier to be appreciated as being in the file scope, it has to be declared around the top of the program. It does not have to be declared inside any block. The scope of a file scope identifier, ends at the end of the program. That is, a file scope terminates at the end of the file (end of the translation unit).

Issue of Initialization in File Scope, outside Function Body
In the file scope, a declaration can occur with initialization, in one statement. Read and test the following program:

    #include <stdio.h>
    
    int myInt = 5;
   
    int main(int argc, char *argv[])
        {
            
            printf("%i\n", myInt);

            return 0;
        }

The program works; the output is 5; declaration and initialization, occurred in one statement in the file scope. Now, read and test the following program, where the declaration and initialization have been separated into two statements, in the file scope:

    #include <stdio.h>
    
    int myInt;
    myInt = 5;
   
    int main(int argc, char *argv[])
        {
            
            printf("%i\n", myInt);

            return 0;
        }

Compilation stopped; and an error message was issued. This is because, declaration and initialization, in two different statements, is not allowed in file scope. However, the initialization can be done in the function scope, even if it is the C main() function. Read and test the following code:

    #include <stdio.h>
    
    int myInt;

    int main(int argc, char *argv[])
        {
            myInt = 5;
   
            printf("%i\n", myInt);

            return 0;
        }

Compilation completes; the program works; the output is 5.

Related Links

More Related Links

Cousins

BACK NEXT

Comments