declaration

PROGRAMMAZIONE I
A.A. 2016/2017
DEFINITION AND DECLARATION
Defining something means providing all of the necessary
information to create that thing in its entirety.
üDefining a function means providing a function body;
üDefining a variable means allocating memory for it.
Definitions are also declarations
Most of the time, when you declare a variable, you are
also providing the definition. What does it mean to define
a variable, exactly? It means you are telling the compiler
where to create the storage for that variable.
DIFFERENCE
int func();
int main()
{
int x = func();
}
func is declared
x is defined
int x;
int main()
{
x = 3;
}
x is defined
int main()
{
static int x = 3;
}
x is defined
extern int x;
int func()
{
x = 3;
}
x is declared
ONE MORE EXAMPLE
extern int x;
int func()
{
x = 3;
}
int x;
x is declared here!
x is defined here!
You have a declaration of x at the top of the program and a definition
at the bottom.
But usually extern is used when you want to access a global variable
declared in another source file.
IN SUMMARY
A declaration provides basic attributes of a symbol: its
type and its name.
A definition provides all of the details of that symbol--if
it's a function, what it does; if it's a variable, where that
variable is stored.
Often, the compiler only needs to have a declaration for
something in order to compile a file into an object file,
expecting that the linker can find the definition from
another file. If no source file ever defines a symbol, but it
is declared, you will get errors at link time complaining
about undefined symbols.
There can be only one definition of the same
variable, but many declarations of it (anywhere it is
used)
DIFFERENCE
A variable declaration says, "there is a variable with the
following name and type in the program".
A variable definition says, "Dear Mr. Compiler, please
allocate memory for a variable with the following name
and type now."
IN A TYPE DEFINTION
Struct Client is a type definition, you do not allocate
anything in memory.
clientArray and clientPtr are variable definitions: memory
is allocated.
struct Client { char name[64],
pin[16],
short id }; // A structure type.
int main() {
struct Client clientArray[100], *clientPtr = clientArray;
// Commands;
}
DECLARATIONS
DECLARATIONS
A declaration determines the significance and properties
of one or more identifiers.
The identifiers you declare are the names of objects,
functions, types, or other things, such as enumeration
constants.
Identifiers of objects and functions can have various
types and scopes.
The compiler needs to know all of these characteristics
of an identifier before you can use it in an expression.
üFor this reason, each translation unit must contain a
declaration of each identifier used in it.
EXAMPLES
The declarator list in the first line contains two
declarators, one of which includes an initializer. The line
declares two objects, iVar1 and iVar2, both with type int.
iVar2 begins its existence with the value 10.
Remember that * goes with the identifier.
int iVar1, iVar2 = 10;
static char msg[] = "Hello, world!";
int a=3, *q=&a;
EXAMPLES
The second line of the next example defines an array
named clientArray with 100 elements of type struct
Client, and a pointer to struct Client named clientPtr,
initialized with the address of the first element in
clientArray:
struct Client { char name[64], pin[16]; /* ... */ }; // A structure type.
struct Client clientArray[100], *clientPtr = clientArray;
Next you see a declaration of a float variable, x, and an
array, flPtrArray, whose 10 elements have the type
pointer to float. The first of these pointers, flPtrArray[0], is
initialized with &x; the remaining array elements are
initialized as null pointers.
üfloat x, *flPtrArray[10] = { &x };
STORAGE CLASS SPECIFIERS
A storage class specifier in a declaration modifies the
linkage of the identifier declared, and the storage
duration of the corresponding objects.
A frequent source of confusion in regard to C is the fact
that linkage, which is a property of identifiers, and
storage duration, which is a property of objects, are
both influenced in declarations by the same set of
keywords—the storage class specifiers.
The storage duration of an object can be automatic,
static, or allocated, and the linkage of an identifer can
be external, internal, or none.
Remember: objects have storage duration, not linkage;
and identifiers have linkage, not storage duration.
STORAGE CLASS
No more than one storage class specifier may appear in
a declaration.
Function identifiers may be accompanied only by the
storage class specifier extern or static.
Function parameters may take only the storage class
specifier register.
AUTO
auto: Objects declared with the auto specifier have
automatic storage duration.
This specifier is permissible only in object declarations
within a function.
In ANSI C, objects declared within a function have
automatic storage duration by default, and the auto
specifier is archaic.
int main(void) {
int a;
{
int count;
auto int month;
}
a++;
}
REGISTER
register: You can use the specifier register when
declaring objects with automatic storage duration.
The register keyword is a hint to the compiler that the
object should be made as quickly accessible as
possible—ideally, by storing it in a CPU register.
However, the compiler may treat some or all objects
declared with register the same as ordinary objects with
automatic storage duration.
In any case, programs must not use the address
operator on objects declared with the register specifier.
EXAMPLE
{
register int miles;
}
{
register int miles;
int* p= &miles;
}
STATIC
Static: a function identifier declared with the specifier
static has internal linkage.
In other words, such an identifier cannot be used in
another translation unit to access the function.
An object identifier declared with static has either no 1)
linkage or 2) internal linkage, depending on whether the
object’s definition is 1) inside a function or 2) outside all
functions.
Objects declared with static always have static storage
duration. Thus the specifier static allows you to define
local objects—that is, objects with block scope—that
have static storage duration.
EXAMPLE
write.c
main.c
static int count=5;
void write_extern();
int main() {
write_extern();
}
#include<stdio.h>
extern int count;
void write_extern(void)
{
printf("count is %i\n", count);
}
gcc -o main write.c main.c
Undefined symbols for architecture x86_64:
"_count", referenced from:
_write_extern in write-e81d6a.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
EXAMPLE
write.c
main.c
int count= 5;
void write_extern();
int main() {
write_extern();
}
#include<stdio.h>
extern int count;
static void write_extern(void)
{
printf("count is %i\n", count);
}
gcc -o main write.c main.c
MacBook-Francesco:ProgrammI francescosantini$ gcc -o main main.c write.c
Undefined symbols for architecture x86_64:
"_write_extern", referenced from:
_main in main-a3af3a.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
STATIC STORAGE
Objects that are defined outside all functions, or within a
function and with the storage class specifier static, have
static storage duration.
All objects with static storage duration are generated and
initialized before execution of the program begins.
(initialized to 0 automatically)
Their lifetime spans the program’s entire runtime.
EXAMPLE
#include<stdio.h>
void func(void);
static int count=10;
/* Global variable - static is the default */
int main() {
while (count--)
func();
}
void func( void )
{
static int i = 5;
i++;
printf("i is %d and count is %d\n", i, count);
}
i is 6 and count is 9
i is 7 and count is 8
i is 8 and count is 7
i is 9 and count is 6
i is 10 and count is 5
i is 11 and count is 4
i is 12 and count is 3
i is 13 and count is 2
i is 14 and count is 1
i is 15 and count is 0
EXAMPLE 2
#include<stdio.h>
void func(void);
Static storage is the default for
variables outside functions
int count=10;
int main() {
while (count--)
func();
}
void func( void )
{
static int i = 5;
i++;
printf("i is %d and count is %d\n", i, count);
}
i is 6 and count is 9
i is 7 and count is 8
i is 8 and count is 7
i is 9 and count is 6
i is 10 and count is 5
i is 11 and count is 4
i is 12 and count is 3
i is 13 and count is 2
i is 14 and count is 1
i is 15 and count is 0
EXAMPLE 3
#include<stdio.h>
void func(void);
int count=10;
int main() {
while (count--)
func();
i++;
}
i static but not visible in main
void func( void )
{
static i = 5;
i++;
printf("i is %d and count is %d\n", i, count);
}
MacBook-Francesco:ProgrammI francescosantini$ gcc -std=c99 -o test test.c
test.c:11:4: error: use of undeclared identifier 'i'
i++;
^
1 error generated.
EXAMPLE 4
#include<stdio.h>
void func(void);
int main() {
while (count--)
func();
}
count visible from here
int count=10;
void func( void )
{
static i = 5;
i++;
printf("i is %d and count is %d\n", i, count);
}
MacBook-Francesco:ProgrammI francescosantini$ gcc -std=c99 -o test test.c
test.c:8:11: error: use of undeclared identifier 'count'
while (count--)
^
1 error generated.
AGAIN MEMORY
EXTERN
Extern: Function and object identifiers declared with the
extern specifier have external linkage:
üYou can use them anywhere in the entire program.
The compiler treats function declarations without a
storage class specifier as if they included the specifier
extern.
Similarly, any object identifiers that you declare outside
all functions and without a storage class specifier have
external linkage.
COMPILER AND LINKER
file1.c
file1.o
file_ex
file2.c
file2.o
gcc -c file1.c
gcc -o file_ex file1.o file2.o
gcc -c file2.c
COMPILER AND LINKER
file1.c
file1.o
file_ex
file2.c
file2.o
gcc -o file_ex file1.c file2.c
EXAMPLE
write.c
main.c
#include<stdio.h>
int count=5;
void write_extern();
extern int count;
int main() {
write_extern();
}
void write_extern(void)
{
printf("count is %i\n", count);
}
gcc –o main main.c
Undefined symbols for architecture x86_64:
"_write_extern", referenced from:
_main in main-95ef15.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
EXAMPLE
write.c
main.c
#include<stdio.h>
int count=5;
void write_extern();
int main() {
write_extern();
}
extern int count;
void write_extern(void)
{
printf("count is %i\n", count);
}
gcc –c write.c
With our without it is the same
OK!
count is 5
TENTATIVE DEFINITION
If you declare an object outside of all functions, without
an initializer and without the storage class specifier
extern, the declaration is a tentative definition.
A tentative definition of an identifier remains a simple
declaration if the translation unit contains another
definition for the same identifier.
If not, then the compiler behaves as if the tentative
definition had included an initializer with the value zero,
making it a definition.
EXAMPLE OF TENTATIVE
DEFINITION
main.c
main.c
#include<stdio.h>
#include<stdio.h>
int count= 3;
int count= 4;
int count;
int count= 4;
int main (void) {
printf("count is %i\n", count);
}
gcc main.c
OK!: count is 4
int main (void) {
printf("count is %i\n", count);
}
gcc main.c
MacBook-Francesco:ProgrammI francescosantini$ gcc write.c
write.c:5:5: error: redefinition of 'count'
int count= 4;
^
write.c:4:5: note: previous definition is here
int count= 3;
^
1 error generated.
EXAMPLE
write.c
main.c
#include<stdio.h>
static int count=5;
void write_extern();
int main() {
write_extern();
}
static int count;
void write_extern(void)
{
printf("count is %i\n", count);
}
gcc –c write.c
count is 0
OK!
EXAMPLE
write.c
main.c
#include<stdio.h>
int count=5;
void write_extern();
int main() {
write_extern();
}
extern int count;
void write_extern(void)
{
printf("count is %i\n", count);
}
gcc –o write write.c
Undefined symbols for architecture x86_64:
"_count", referenced from:
_write_extern in write-713128.o
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
EXAMPLE
write.c
main.c
int count=5;
void write_extern();
int main() {
write_extern();
}
gcc -o main write.c main.c
OK!
#include<stdio.h>
extern int count;
void write_extern(void)
{
printf("count is %i\n", count);
}
DECLARATIONS AND DEFINITIONS
You can declare an identifier as often as you want,
but only one declaration within its scope can be a
definition.
Placing the definitions of objects and functions with
external linkage in header files is a common way of
introducing duplicate definitions, and is therefore not a
good idea.
An identifier’s declaration is a definition in the following
cases:
CASE 1
A function declaration is a definition if it contains the
function block.
int iMax( int a, int b );
// This is a declaration, not a definition.
// a and b are not defined
int iMax( int a, int b )
// This is the function's definition. a and
// b are defined
{
return ( a >= b ? a : b );
}
CASE 2
An object declaration is a definition if it allocates storage
for the object.
Declarations that include initializers are always
definitions.
Furthermore, all declarations within function blocks are
definitions unless they contain the storage class specifier
extern.
extern int a = 10;
extern double b[];
void func() {
extern char c;
static short d;
float e;
/* ... */
}
// Definition of a.
// Declaration of the array b, which is
// defined elsewhere in the program.
// Declaration of c, not a definition.
// Definition of d.
// Definition of e.
LINKAGE
An identifier with external linkage represents the same
function or object throughout the program. The compiler
presents such identifiers to the linker, which resolves
them with other occurrences in other translation units
and libraries.
An identifier with internal linkage represents the same
object or function within a given translation unit. The
identifier is not presented to the linker. As a result, you
cannot use the identifier in another translation unit to
refer to the same object or function.
No linkage
üFunction parameters
üObject identifiers that are declared within a function and
without the storage class specifier extern
EXAMPLE (LINKAGE)
int func1( void );
int a;
extern int b;
static int c;
external
external
external
internal
static void func2( int d ) {
extern int a;
int b = 2;
static int e;
extern int c;
/* ... */
}
func2 internal, d no linkage
external, this a is the same as above
no linkage
no linkage
c is the same as the c above: internal
void fun3 (void) {
/* commands */
}
external
EXAMPLE (DEFINITION OR
DECLARATION)
int func1( void );
int a= 3;
int ;
extern int b = 1;
static int c;
declaration
definition
attempt of definition
definition
definition
static void func2( int d ) {
extern int a;
int b = 2;
static int e;
/* ... */
}
func2 definition, d definition
declaration
definition
definition
Since b is external by default, if it is defined in a
different translation unit, then this is a declaration,
otherwise it is a definition.
TYPEDEF DECLARATION
MAKE IT SIMPLE!
The easy way to use types with complex names is to
declare simple synonyms for them.
In a declaration that starts with the keyword typedef,
each declarator defines an identifier as a synonym for
the specified type.
The identifier is then called a typedef name for that type.
Except for the keyword typedef, the syntax is exactly the
same as for a declaration of an object or function of the
specified type.
EXAMPLES
In the scope of these declarations,
üUINT is synonymous with unsigned int, and
üPoint_t is synonymous with the structure type struct Point.
typedef unsigned int UINT;
typedef struct Point { double x, y; } Point_t;
typedef enum state {DEAD,ALIVE} State
The variable ui has the type unsigned int, and uiPtr is a
pointer to unsigned int.
üUINT ui = 10, *uiPtr = &ui;