All code generating files use the following general structure. Typically these are C files with an extension of ".c
".
/** @fileDefinitions of bizarre protocols for something.​This is a detailed description of the something for which thisfile exists. Something rotates upon the blarvitz allowingimplementation of bizarre protocols. If the blarvitz is NULL,describe what is happening in more detail. If non-NULL, thenyou should probably also explain your rationale.​Copyright (c) 20XX, Acme Corporation. All rights reserved.<BR>SPDX-License-Identifier: BSD-2-Clause-Patent​@par Specification Reference:- UEFI 2.3, Chapter 9, Device Path Protocol- PI 1.1, Chapter 10, Boot Paths**//* Include necessary header files here */#include <DxeCore.h>#include <Uefi.h>#include "SampleThing.h"​/* Define external, global and module variables here */​/* Function Definitions */​/* If this is a protocol definition, theprotocol structure is defined and initialized here.*/
There are three components of the C language that interact to affect when a particular variable can be used, or not.
Scope: The context in which an identifier is visible constitutes the scope of that identifier..
Visibility: If an identifier can be referenced within a particular scope it is said to be visible.
Storage Class: Allocation of storage space is controlled by the storage class of an identifier.
Understanding the C scoping rules is critical for good programming. The various identifier scopes encountered are: file, prototype, function, and block or local scope.
File Scope
Top-level identifiers and preprocessor macros are said to have file scope. Their scope extends from their declaration point to the end of the source program file. A preprocessor macro can go out-of-scope earlier if a #undef
command that cancels its definition is encountered.
Prototype Scope
The identifiers making up the formal parameters in function prototypes have prototype scope. Identifiers with prototype scope have scopes extending from their declaration point to the end of the prototype.
Function Scope
Statement labels have function scope. Function scope identifiers have scope which encompasses the entire function body in which they appear. This means that statement labels can be referenced before they are declared.
Block (local) Scope
Formal parameters in function definitions and data defined within compound statements have block scope. Any group of statements that are encompassed within a pair of braces, { }, is a compound statement. The body of a function is also a compound statement. Compound statements can be nested, with each creating a new scope.
Data declarations may follow the opening brace of a compound statement, regardless of nesting depth, and before any code generating statements have been entered. Other than at the outermost block of a function body, this type of declaration is strongly discouraged.
Note: Visual C++ gives all structure declarations File Scope, even though ISO/IEC 9899 specifies that structure declarations may have Block Scope.
A declaration of an identifier is visible in some context if a use of the identifier in that context is associated with that declaration. A declaration might be visible throughout its scope, but it might also be hidden by other declarations whose scope and visibility overlap that of the first declaration.
Identifiers with file scope have the outermost scope. Those with block scope have the innermost scope. Nesting successive blocks creates more inner scopes.
Only disallow instances where an outer definition is hidden by a second inner definition. This rule is not violated when the first definition is not hidden by the second definition.
The following example shows how the scope visibility of identifiers can overlap and hide each other. Never write code that does this.
1 UINTN MyVar = 7; // File scope23 VOID4 MyFunction (5 OUT UINT32 *MyVar // Function scope6 )7 {8 UINT32 i;910 for ( i = 0; i < 5; ++i) {11 UCHAR8 MyVar = i; // Block scope12 INT16 i = 12;1314 MyVar += 'A';15 process ( MyVar, i);16 }17 *MyVar = i;18 }1920 main()21 {22 UINT32 George = 4;2324 MyFunction ( &George);25 process ( MyVar, 0);26 }27
In the above example, there are three declarations of MyVar
:
On line 1, MyVar
is defined with file scope and value 7 This scope extends from line 1 through line 26.
On line 5, MyVar
is declared as a formal parameter of MyFunction. Its context extends from line 5 through the end of the function on line 18.
On line 11, MyVar
is declared with block scope. From its declaration point on line 11 through the end of the block in which it was declared, this declaration of MyVar
will be in scope. It is initialized to the current value of i
.
To test yourself on your understanding of scope and visibility rules, determine the value and associated declaration for each use of MyVar
before reading further.
The first use of MyVar
is on line 14 Its associated declaration is on line 11 Each time through the for loop it will have the values 'A', 'B', 'C', and 'D' in succession.
On line 15, MyVar
is used again with its associated declaration on line 11 with the same succession of values assigned as on line 14.
MyVar
, on line 17, associates with the formal parameter declaration on line 5 In this case the value of i
, which is 5, is stored in the location MyVar
points to. The location is established in the call to MyFunction
on line 24.
The last use of MyVar
occurs on line 25 This occurrence associates with the declaration on line 1 and has the value 7.
Things get extremely tricky on line 12 The identifier i
, which is the control variable for the enclosing for
loop, is declared with a different type and value. It is ambiguous as to which i
was wanted in line 11. How does this affect the for
loop? What will be the value of i
at lines 15 and 17?
The scope rules tell us the answers, but they still aren't obvious.
The i
on line 10 is outside the block, which is the body of the for
loop. It associates with the declaration on line 8, so the for
loop will continue to work as expected.
On line 11, MyVar
will be initialized to the current value of i
. It is compiler dependent whether this is the first value of i
, 0, or the value of i
for each loop.
Line 12 declares a new variable i
which is given the value 12 C scoping rules state that a scope begins at the declaration point, not at the beginning of the block in which the declaration appears. Because of this, the i
used on line 11 is not the same i
declared here.
The value of i
used on line 15 is 12, the value of the associated and currently visible identifier with that name.
As you can see, using the same identifier at different scopes can be confusing at best. All it takes is one mistake and a bug is introduced that is very hard to locate.
So far we have been talking about variable and function identifiers, which have an existence at run time. However, the scope and visibility rules apply equally to identifiers applied to objects that don't necessarily exist at run time: typedef
names, type tags, enumeration constants, macros, and labels.
Macros and labels follow the rules outlined for them in Section 5.4.1.1 "Scope". The other compile-time names follow the same scope and visibility rules as any variable defined at the same location.
A special case of scope and visibility is the external identifier, also called an identifier with external linkage. All instances of an external identifier, among all the files making up a C module, are forced to refer to the same object or function. Each instance of the external identifier must be declared with the same type in each file or else the result is undefined.
External names are declared using the extern
keyword. In C89, and earlier dialects, external identifiers declared at file scope are implicitly extern. But, not all identifiers declared extern
were external.
The compilers supported by EDK II follow the C99 specification for extern
. All declarations of the external variable must use the extern
keyword. There must be a single definition of the external variable which does not use the extern
keyword. This definition may also include an initializer.
By convention, external names are declared at the top level of a C program and therefore have file scope. For EDK II, external names must be declared in a single header file and must be defined at the top level of a C file as specified in Section 5.4.1.3 "Compile-Time Names".
Thus, while it might be legal C, do not declare external variables anywhere other than at the top level of a file as specified by this document.
An object declared STATIC
has either file or block scope.
Throughout the set of source files defined within a single .inf file, do not reuse an identifier with static storage duration. The compiler may not be confused by this, but the user may confuse unrelated variables with the same name.
Some source-level debuggers are unable to resolve static functions. Until it can be verified that no one is dependent upon a debugger with this limitation, it is strongly recommended that functions not be declared static.