- Forward


Scope, Storage Duration, and Linkage in C
an Introduction


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu

Print

Scope, Storage Duration, and Linkage
Back SMYC Forward
  • Scope:
    • The portion of a translation unit (i.e., the code produced by the preprocessor from the source file and the header files) in which an identifier is visible (i.e., represents an entity)
  • Storage Duration:
    • The (minimum potential) lifetime of the storage containing an entity
  • Linkage:
    • Determines whether declarations in different scopes can refer to the same entity
Declarations in C
Back SMYC Forward
  • Purpose:
    • Associates an entity with a name, either introducing a new name into the translation unit or re-using a name from earlier (in the case of a redeclaration)
  • Loose Syntax:
    • [qualifier] specifiers declarator [= initializer];
  • Kinds of Specifiers:
    • Type specifiers (e.g., char, int, void)
    • Function specifiers (e.g., inline)
    • Storage class specifiers (e.g., auto, extern, register, static)
    • Other Specifiers (e.g., struct, union, enum)
Examples of Declarations
Back SMYC Forward
// Type Specifier int i; // Type Specifier with a Modifier double grades[10]; void error_exit(); // Type Specifier with a Type Qualifier volatile int size; // Type Specifier with a Type Qualifier and Modifier const int *fp;
Scope in C
Back SMYC Forward
  • File Scope:
    • Identifiers that are declared in the outermost part of a translation unit
  • Function Prototype Scope:
    • Identifiers that are declared in the formal parmeter list of a function
  • Block Scope:
    • Identifiers declared within a function
    • Identifiers declared within a block
Scope in C (cont.)
Back SMYC Forward
#include <stdio.h> int main(void) { n += 5; printf("%d\n", n); return 0; }
  • Compile?
    • No, because n is not declared in the scope of main()
      Expand
  • Output?
    • N/A
      Expand
Scope in C (cont.)
Back SMYC Forward
#include <stdio.h> int n = 0; int main(void) { n += 5; printf("%d\n", n); return 0; }
  • Compile?
    • Yes, because n has file scope
      Expand
  • Output?
    • 5
      Expand
Scope in C (cont.)
Back SMYC Forward
#include <stdio.h> int n = 0; int main(void) { int n; n += 5; printf("%d\n", n); return 0; }
  • Compile?
    • Yes, there is an n with file scope and an n with local scope
      Expand
  • Output?
    • Garbage, because the local n is not initialized.
      Expand
Scope in C (cont.)
Back SMYC Forward
#include <stdio.h> int n = 0; int main(void) { int n = 1; n += 5; printf("%d\n", n); return 0; }
  • Compile?
    • Yes, there is an n with file scope and an n with local scope
      Expand
  • Output?
    • 6
      Expand
Storage Duration in C
Back SMYC Forward
  • Automatic:
    • Begins at entry into the block and ends upon exit from the block
  • Static:
    • Begins at execution and ends at termination
  • Dynamic:
    • Begins at allocation (e.g., malloc()) and ends at deallocation (e.g., free())
Using Storage Specifiers to Specify Duration
Back SMYC Forward
c_scope-duration
Storage Duration in C (cont.)
Back SMYC Forward
#include <stdio.h> int total(int x) { static int total = 0; total += x; return total; } int main(void) { total(5); total(10); printf("%d\n", total(15)); return 0; }
  • Compile?
    • Yes, the static specifier can be used in the declaration of a varaible with block scope
      Expand
  • Output?
    • 30
      Expand
Linkage in C
Back SMYC Forward
  • No Linkage:
    • The entity can't be referenced by name from anywhere else
  • Internal Linkage:
    • The entity can be referenced by names declared in the same scope and/or names declared in other scopes of the same translation unit (which is useful if you want to have multiple entities with the same name in different translation units)
  • External Linkage:
    • The entity can also be referenced by name in other translation units (which is useful if you want the entity to be "global")
Specifying Linkage for Functions
Back SMYC Forward
c_linkage_functions
Specifying Linkage for Functions (cont.)
Back SMYC Forward
#ifndef LIBRARY_H #define LIBRARY_H int net_price(int price, int discount); static int validate(int n); #endif
#include "library.h" int net_price(int price, int discount) { return validate(price - discount); } static int validate(int n) { if (n < 0) return 0; else return n; }
Specifying Linkage for Functions (cont.)
Back SMYC Forward
#include "library.h" int main(void) { int i, j; i = validate(-5); j = net_price(6, 2); }
  • Compile?
    • Yes
      Expand
  • Link?
    • No, the reference to validate() in main() is undefined because validate() has internal linkage
      Expand
Specifying Linkage for Variables
Back SMYC Forward
c_linkage_variables
Specifying Linkage for Variables (cont.)
Back SMYC Forward
#include <stdio.h> int n = 0; int main(void) { extern int n; n += 5; printf("%d\n", n); return 0; }
  • Compile?
    • Yes
      Expand
  • Output?
    • 5
      Expand
Definitions in C
Back SMYC Forward
  • Definitions:
    • A definition of an entity allocates storage for it
  • An Important Constraint:
    • An entity must be defined exactly once (in a translation unit if the entity has internal linkage; in the entire program if the entity has external linkage)
  • An Important Point:
    • All definitions are implicitly declarations
Examples of Definitions
Back SMYC Forward
The Following are at the Top Level of a Translation Unit
// A definition (and implicit declaration) of a function int max(int a, int b) { if (a > b) return a; else return b; } // A definition (and implicit declaration) of a variable with file scope int total = 0;
Declarations vs. Definitions (cont.)
Back SMYC Forward
  • Functions:
    • A function heading without a body is a declaration
    • A function heading with a body is both a declaration and a definition
  • Variables with File Scope (i.e., External Variables):
    • The situation is more complicated
Declarations/Definitions of External Variables
Back SMYC Forward
  • Initializer == Yes:
    • Makes the statement a definition (and a declaration)
  • Initializer == No and extern == Yes:
    • Makes the statement a declaration only (with external linkage)
  • Initializer == No and (extern == No or static == Yes):
    • A tentative definition (which acts as a definition with the initializer = 0 or = {0} if there is no definition in the same translation unit)
Declarations/Definitions of External Variables (cont.)
Back SMYC Forward
The Following are at the Top Level of a Translation Unit
// A declaration with external linkage extern int i; // A definition with external linkage int total = 0; // A definition with internal linkage static const char *university = "JMU"; // A tentative definition with external linkage int j; // A tentative definition with internal linkage static int k;
A Common "Problem"
Back SMYC Forward
  • The General Situation:
    • You are creating a library of functions all of which assign a value to a variable with file scope
  • A Specific Example:
    • The library is organized into the files account.h and account.c
    • There is a file named driver.c that uses the library
A Common "Problem": Attempt 1
Back SMYC Forward
#ifndef ACCOUNT_H #define ACCOUNT_H #define ACCT_OK 0 #define ACCT_INVALID 1 #define ACCT_CLOSED 2 int acct_status; #endif
#include "account.h" // Use acct_status
#include "account.h" // Use acct_status
A Common "Problem": Attempt 1 (cont.)
Back SMYC Forward
  • Compile?
    • Yes, because account.h is in both translation units
      Expand
  • Link?
    • No, you will get a message like multiple definition of 'acct_status' because acct_status has been defined in both translation units
      Expand
A Common "Problem": Attempt 2
Back SMYC Forward
#ifndef ACCOUNT_H #define ACCOUNT_H #define ACCT_OK 0 #define ACCT_INVALID 1 #define ACCT_CLOSED 2 // Don't define acct_status since that caused the problem #endif
#include "account.h" // Use acct_status
#include "account.h" // Use acct_status
A Common "Problem": Attempt 2 (cont.)
Back SMYC Forward
  • Compile?
    • No, you will get a message like 'acct_status' was not declared in this scope
      Expand
  • Link?
    • Not Applicable
      Expand
A Common "Problem": Attempt 3
Back SMYC Forward
#ifndef ACCOUNT_H #define ACCOUNT_H #define ACCT_OK 0 #define ACCT_INVALID 1 #define ACCT_CLOSED 2 // Don't define acct_status #endif
#include "account.h" int acct_status; // Use acct_status
#include "account.h" // Use acct_status
A Common "Problem": Attempt 3 (cont.)
Back SMYC Forward
  • Compile?
    • account.c will compile but driver.c won't
      Expand
  • Link?
    • Not Applicable
      Expand
A Common "Problem": Attempt 4
Back SMYC Forward
#ifndef ACCOUNT_H #define ACCOUNT_H #define ACCT_OK 0 #define ACCT_INVALID 1 #define ACCT_CLOSED 2 extern int acct_status; #endif
#include "account.h" // Use acct_status
#include "account.h" // Use acct_status
A Common "Problem": Attempt 4 (cont.)
Back SMYC Forward
  • Compile?
    • Yes, because acct_status is only declared in account.h, not defined
      Expand
  • Link?
    • No, you'll get a message like undefined reference to 'acct_status' because it has been declared but not defined
      Expand
A Common "Problem": Attempt 5
Back SMYC Forward
#ifndef ACCOUNT_H #define ACCOUNT_H #define ACCT_OK 0 #define ACCT_INVALID 1 #define ACCT_CLOSED 2 extern int acct_status; #endif
#include "account.h" int acct_status = ACCT_OK; // Use acct_status
#include "account.h" // Use acct_status
A Common "Problem": Attempt 5 (cont.)
Back SMYC Forward
  • Compile?
    • Yes
      Expand
  • Link?
    • Yes
      Expand
There's Always More to Learn
Back -