chapter 5 Class 3,4
5.3 The static Storage Class Specifier
The keyword static is a storage class specifier, but it is perhaps better viewed as a storage class qualifier as it imparts di erent properties depending on whether an object is a local variable, an
external variable, or a function. Local variables keep their local visibility but gain static extent.
They are initialised to zero by default and retain their values between function calls.
int increment(void)
{
static int local_static; /* local scope, static extent, initial value 0 */ return local_static++; /* 1st call will return: 0, 2nd: 1, 3rd: 2, ... */
}
External variables and functions that are qualified as static obtain file scope, which means their visibility is limited to a single source file. Their names are not exported to the linker and are not visible to object modules of other source files.3 This prevents unwanted access by code in other parts of the program and reduces the risk of naming conflicts. For example, the following declarations are unrelated and non-conflicting.
File one.c:
static double myvariable;
static void myfunc(int idx);
File two.c:
static int myvariable; /* no conflict with file one.c */
static int myfunc(int idx); /* no conflict */
The example of getch() and ungetch() in the previous section is one situation where static variables would constitute better design. The two functions would remain extern as they might be called from functions in other files, but the variables buffer and bufidx only require file-scope. Thus, static is to preferred over extern where possible as it permits more modular design. As a rule, and where possible, static functions are preferred over external functions, static variables are preferred over external variables, and local variables are preferred over static variables.
Aside. Many programs today operate using multiple threads of control, meaning that various parts of the program operate concurrently, or over a timeslice so as to appear concurrent. In such programs, excessive caution is required while using works that rely on stable or external variables. Temporal dependencies on the value of external variables may be violated as the di erent threads are switched in and out. In general, variables with static border are best protected in multi-threaded programs (unless they have been clearly synchronized).
5.4 Scope Resolution and Name Hiding
It is possible for a program to have two variables of the same name with overlapping scope such that they are potentially visible from the same place. In such situations, one variable will hide the other. C specifies that the variable with more restricted scope will be visible, and the other hidden. In other words, the variable that is “more local” has dominant visibility. This situation is demonstrated in the following program.
1 #include <stdio.h>
2 int modify(int, int);
3 int x=1, y=3, z=5; /* external variables */
4 int main(void)
Note -- To summarise, for local variables, the keyword static changes the extent but does not alter scope while, for external variables and functions, it changes the scope but does not alter the extent.
5 {
6 int x, z=0; /* local scope 1 */
7 x = y + 1;
8 while (x++ < 10) {
int x, y = 0; /* local scope 2 */
9 x = z % 5;
printf("In loop \tx= %d\ty= %d\tz= %d\n", x, y++, z++);
10 }
11 printf("Before modify()\tx= %d\ty= %d\tz= %d\n", x, y, z);
12 z = modify(x, y);
13 printf("After modify()\tx= %d\ty= %d\tz= %d\n", x, y, z);
14 }
15 int modify(int a, int b)
16 {
17 z += a + b;
18 return z;
19 }
top of the block in which it is defined, a further non-intuitive name-hiding situation can occur. For example, consider the following code segment.
float i = 5.f;
{
int j = i;
int i = 0;
...
}
This program produces the following output.
In loop x= 0 y= 0 z= 0
In loop x= 1 y= 0 z= 1
In loop x= 2 y= 0 z= 2
In loop x= 3 y= 0 z= 3
In loop x= 4 y= 0 z= 4
In loop x= 0 y= 0 z= 5
Before modify() x= 11 y= 3 z= 6
After modify() x= 11 y= 3 z= 19
This output is a result of the C name-hiding rules. A brief discussion of the above example may help clarify their properties.
Identifier x refers to x-local-scope-1, and y to y-global-scope.
This line represents one of the more di cult instances of name-hiding: does x refer to the inner local block or the outer one? In fact, X-X-local-scope refers to -1, not x-local-scope-2, which has been declared within a mixed statement after that time, and not conditionally for itself is.
Identifiers x and y refer to the inner local-scope-2, and z refers to the outer local-scope-1.
Identifiers x and z refer to the outer local-scope-1, and y refers to the global-scope.
Identifiers a and b are local to the function block, while z is a global variable.
The problem of name hiding doesn’t end there. Because C’s scoping rules specify that the scope of an identifier begins at its point of declaration rather than the
Intuitively, one might think that j would be initialised by the inner-most i, the int .However, it has come to the notice that Int i is not in existence unless it is declared under Jammu, so that the Junk's manifesto can be started with only one: float i = 5.f.
Name-hiding issues are easily avoided by defining appropriate variable names. It is bad practice to rely on the scope-resolution rules, and name-hiding leads to confusing and error-prone code. Avoid same-name identifiers for functions or variables that might share the same scope.