edits: 01/26/2008
These are additional puzzles that deal with linkage. For reference, here again are the rules for linkage.
Linkage determines how multiple occurrences of the same identifier refer to the same or different entities. There are three kinds of linkage: no linkage, external linkage, and internal linkage.
1. No Linkage:
1.1 Each declaration of an identifier with no linkage denotes a unique entity. If there are several entities in the project with the same name and no linkage, scope determines which one a statement refers to.
1.2 The following have no linkage:
- an identifier declared to be anything other than a variable or a function,
- a function parameter,
- a identifier declared inside a block (and not explicitly declared external.)
2. External Linkage:
2.1 Several source files and libraries may declare a particular identifier with external linkage. Each declaration of that particular identifier denotes the same entity. It is the job of the linker to ensure that there is just one entity for that identifier in the executable file.
2.2 A variable declared outside of any function has external linkage unless otherwise specified. Sometimes variables declared like this are called global variables.
2.3 A function name has external linkage unless otherwise specified.
2.4 The keyword
extern
gives external linkage to an identifier and gives access to an entity defined in another file. If an identifier would already have external linkage, declaring itextern
does not affect it.
3. Internal Linkage:
3.1 An identifier that has internal linkage is unique within its source file, but is invisible to other source files. If other source files use this same identifier, they refer to other entities.
3.2 The keyword
static
gives internal linkage to an identifier that otherwise would have external linkage. (This is the only way to get internal linkage.)
To show linkage in action, you must create a project that consists of several files. Here are three files:
/* --- fileA.c --- */ int x = 10;
/* --- fileB.c --- */ int x; static int bar(int val) { printf(" static bar.\n"); return 2*val; } foo() { printf(" entering foo.\n"); x = bar(x); printf(" exiting foo.\n"); }
/* --- fileC.c --- */ int x; bar() { printf(" entering extern bar x: %d\n", x); x += 10; printf(" exiting extern bar x: %d\n", x); } main() { printf("entering main x: %d\n", x ); bar(); foo(); printf("exiting main x: %d\n", x ); system("pause"); }
Remember that function names are identifiers, so the rules for the linkage of identifiers applies to them.
Question: What does the above project write to the monitor when it is executed?
What does the following code write to the monitor? (Make a plausible guesses about the numbers.)
* --- goodMath.c --- */ #include <stdio.h> #include <math.h> void goodMath( double angle ) { printf("good cos( %lf ) = %lf\n", angle, cos(angle) ); printf("good sin( %lf ) = %lf\n", angle, sin(angle) ); }
/* --- main.c --- */ static double sin( double x ) { return x; } static double cos( double x ) { return 1 - x/2.0; } void badMath( double angle ) { printf("bad cos( %lf ) = %lf\n", angle, cos(angle) ); printf("bad sin( %lf ) = %lf\n\n", angle, sin(angle) ); } main() { double angle = .05 ; /* radians */ badMath ( angle ); goodMath( angle ); system("pause"); }
The functions cos()
and sin()
are
contained in the standard math library, which will automatically
be linked into the executable when you compile and run the above program.
However, the file main.c has its own cos()
and sin()
,
which, since they have internal linkage, will not conflict with the other file.
What does the following code write to the monitor?
/* --- rect.h --- */ void setWidth( int w ); void setHeight( int h ); int getArea();
/* --- rect.c --- */ static int width = 0; static int height = 0; void setWidth( int w ) { width = w; } void setHeight( int h ) { height = h; } int getArea() { return height*width; }
/* --- mainRect.c --- */ #include <stdio.h> #include "rect.h" int main() { setHeight( 4 ); setWidth( 3 ); printf("Area: %d\n", getArea() ); system("pause"); }
The header file rect.h is useful so that mainRect.c does not need to have explicitly list all the function prototypes.
Will the following code compile?
/* --- rect.h --- */ void setWidth( int w ); void setHeight( int h ); int getArea();
/* --- rect.c --- */ static int width = 0; static int height = 0; void setWidth( int w ) { width = w; } void setHeight( int h ) { height = h; } int getArea() { return height*width; }
/* --- mainRect.c --- */ #include <stdio.h> #include "rect.h" int main() { height = 4; width = 3; printf("Area: %d\n", getArea() ); system("pause"); }
Note that extern
has been removed
from some of the declarations.
Does the following fix the problem? Will it compile and run?
/* --- rect.h --- */ void setWidth( int w ); void setHeight( int h ); int getArea();
/* --- rect.c --- */ static int width = 0; static int height = 0; void setWidth( int w ) { width = w; } void setHeight( int h ) { height = h; } int getArea() { return height*width; }
/* --- mainRect.c --- */ #include <stdio.h> #include "rect.h" extern int height; extern int width; int main() { height = 4; width = 3; printf("Area: %d\n", getArea() ); system("pause"); }
Add the function getPerimeter()
to the
following project:
/* --- rect.h --- */ void setWidth( int w ); void setHeight( int h ); int getArea();
/* --- rect.c --- */ static int width = 0; static int height = 0; void setWidth( int w ) { width = w; } void setHeight( int h ) { height = h; } int getArea() { return height*width; }
/* --- mainRect.c --- */ #include <stdio.h> #include "rect.h" int main() { setHeight( 4 ); setWidth( 3 ); printf("Area: %d Perimeter: %d\n", getArea(), getPerimeter() ); system("pause"); }
Think of this as adding the method getPerimeter()
to a class (in object oriented programming).
What does the following project write to the monitor?
/* --- stack.h --- */ void push( int item ); int pop(); int empty(); int full();
/* --- stack.c --- */ #define stackSize 16 static int stack[stackSize]; /* top indexes the item at the top of the stack */ static int top = -1; void push( int item ) { top++ ; stack[top] = item; } int pop() { int item; item = stack[top]; top--; return item; } int empty() { return top == -1; } int full() { return top == stackSize; }
/* --- mainStack.c --- */ #include <stdio.h> #include "stack.h" int main() { if ( !full() ) push( 13 ); if ( !full() ) push( -1 ); if ( !full() ) push( 8 ); if ( !full() ) push( 12 ); if ( !empty() ) printf("%d\n", pop() ); if ( !empty() ) printf("%d\n", pop() ); if ( !empty() ) printf("%d\n", pop() ); system("pause"); }
When a variable with block scope is declared to be static
,
it is implemented in the same section of memory as variables that have file scope
(those variables declared outside of any function).
This section of memory is sometimes called "static memory".
Otherwise, variables with block scope are implemented on the run-time stack.
Variables implemented in static memory are persistent. They remain in existence for as long as the program is running. A value assigned to a variable in static memory will be available anywhere the variable is in scope. The value will not change unless another value is assigned to it.
A variable implemented on the run-time stack is created (pushed onto the stack) when control enters the variable's block. It is destroyed (popped off the stack) when control leaves the block. Such variables are called automatic because they are automatically created on entry to a block and automatically destroyed on exit from a block. Values assigned to an automatic variable are available only when control is inside the variable's block.
If a static
variable inside a block is initialized,
that initialization is done only once, the first time control enters the block.
If a value is assigned to the variable later in the block, that value will be retained
even after control leaves the block. The initialization
will not be performed the next time control enters the block.
If an automatic variable inside a block is initialized, that initialization is done every control enters the block. If a value is assigned to the variable later in the block, that value will be lost when after control leaves the block.
static
variables are initialized to zero, by default.
Automatic variables are not initialized by default.
They should be initialized in the declaration or with explicit assignment statements.
Here is a table that summarizes some of these rules:
Location of Variable Declaration | Scope | Linkage |
Storage |
||
variable not declared as static |
variable is declared as static |
variable not declared as static |
variable is declared as static |
||
outside of any block (global variable) |
File Scope | External |
Internal Linkage |
Static |
Static |
inside a block (local variable) |
Block Scope | No Linkage |
No Linkage |
Automatic (on the stack) |
Static |
in a parameter list | Block Scope | No Linkage |
--- |
Automatic (on the stack) |
--- |
What does the following write to the monitor?
#include <stdio.h> void foo() { int var=0; static int stVar=0; var++ ; stVar++ ; printf( "var: %d stVar: %d\n", var, stVar ); } main() { foo(); foo(); foo(); system("pause"); }
What does the following write to the monitor?
#include <stdio.h> int nextPrime() { static int primes[] = { 2, 3, 5, 7, 11, 13, 17, 23 }; static int index = -1; index++ ; return primes[index]; } int main() { printf("prime: %d\n", nextPrime() ); printf("prime: %d\n", nextPrime() ); printf("prime: %d\n", nextPrime() ); system("pause"); }
What does the following write to the monitor?
#include <stdio.h> static int x = 44; foo() { int j = 0, x = 0; { static int x = 100; printf(" inner x: %d\n", x ); x++ ; } printf(" outer x: %d\n", x ); } main() { int x; for ( x=10; x<15; x++ ) { printf("main x: %d\n", x ); foo(); } system("pause"); }
This puzzle involves a number of scoping rules and linkage rules.
As always, try to figure out how many varialbes x
there are and where they can be seen.