Programming languages — C (INTERNATIONAL STANDARD ISO/IEC 9899:TC3) - page 6

 

  Главная      Manuals     Programming languages — C (INTERNATIONAL STANDARD ISO/IEC 9899:TC3) - 2007 year

 

Search            copyright infringement  

 

 

 

 

 

 

 

 

 

 

 

Content      ..     4      5      6      7     ..

 

 

 

Programming languages — C (INTERNATIONAL STANDARD ISO/IEC 9899:TC3) - page 6

 

 

negative, so the operands of the comparison can never compare equal. Therefore, for full portability, the
variable

c

should be declared as

int

.

5

EXAMPLE 2

In the fragment:

char c;

int i;

long l;

l = (c = i);

the value of

i

is converted to the type of the assignment expression

c = i

, that is,

char

type. The value

of the expression enclosed in parentheses is then converted to the type of the outer assignment expression,
that is,

long int

type.

6

EXAMPLE 3

Consider the fragment:

const char **cpp;

char *p;

const char c = 'A';

cpp = &p;

//

constraint violation

*cpp = &c;

//

valid

*p = 0;

//

valid

The first assignment is unsafe because it would allow the following valid code to attempt to change the
value of the const object

c

.

6.5.16.2 Compound assignment

Constraints

1

For the operators

+=

and

-=

only, either the left operand shall be a pointer to an object

type and the right shall have integer type, or the left operand shall have qualified or
unqualified arithmetic type and the right shall have arithmetic type.

2

For the other operators, each operand shall have arithmetic type consistent with those
allowed by the corresponding binary operator.

Semantics

3

compound assignment of the form

E1

op

= E2

differs from the simple assignment

expression

E1 = E1

op

(E2)

only in that the lvalue

E1

is evaluated only once.

§6.5.16.2 Language

93

6.5.17 Comma operator

Syntax

1

expression:

assignment-expression
expression

,

assignment-expression

Semantics

2

The left operand of a comma operator is evaluated as a void expression; there is a
sequence point after its evaluation. Then the right operand is evaluated; the result has its
type and value.

97)

If an attempt is made to modify the result of a comma operator or to

access it after the next sequence point, the behavior is undefined.

3

EXAMPLE As indicated by the syntax, the comma operator (as described in this subclause) cannot
appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists
of initializers). On the other hand, it can be used within a parenthesized expression or within the second
expression of a conditional operator in such contexts. In the function call

f(a, (t=3, t+2), c)

the function has three arguments, the second of which has the value 5.

Forward references: initialization (6.7.8).

97) A comma operator does not yield an lvalue.

94 Language

§6.5.17

6.6 Constant expressions

Syntax

1

constant-expression:

conditional-expression

Description

2

A constant expression can be evaluated during translation rather than runtime, and
accordingly may be used in any place that a constant may be.

Constraints

3

Constant expressions shall not contain assignment, increment, decrement, function-call,
or comma operators, except when they are contained within a subexpression that is not
evaluated.

98)

4

Each constant expression shall evaluate to a constant that is in the range of representable
values for its type.

Semantics

5

An expression that evaluates to a constant is required in several contexts. If a floating
expression is evaluated in the translation environment, the arithmetic precision and range
shall be at least as great as if the expression were being evaluated in the execution
environment.

6

An integer constant expression

99)

shall have integer type and shall only have operands

that are integer constants, enumeration constants, character constants,

sizeof

expressions whose results are integer constants, and floating constants that are the
immediate operands of casts. Cast operators in an integer constant expression shall only
convert arithmetic types to integer types, except as part of an operand to the

sizeof

operator.

7

More latitude is permitted for constant expressions in initializers. Such a constant
expression shall be, or evaluate to, one of the following:

— an arithmetic constant expression,

— a null pointer constant,

98) The operand of a

sizeof

operator is usually not evaluated (6.5.3.4).

99) An integer constant expression is used to specify the size of a bit-field member of a structure, the

value of an enumeration constant, the size of an array, or the value of a

case

constant. Further

constraints that apply to the integer constant expressions used in conditional-inclusion preprocessing
directives are discussed in 6.10.1.

§6.6 Language

95

— an address constant, or

— an address constant for an object type plus or minus an integer constant expression.

8

An arithmetic constant expression shall have arithmetic type and shall only have
operands that are integer constants, floating constants, enumeration constants, character
constants, and

sizeof

expressions. Cast operators in an arithmetic constant expression

shall only convert arithmetic types to arithmetic types, except as part of an operand to a

sizeof

operator whose result is an integer constant.

9

An address constant is a null pointer, a pointer to an lvalue designating an object of static
storage duration, or a pointer to a function designator; it shall be created explicitly using
the unary

&

operator or an integer constant cast to pointer type, or implicitly by the use of

an expression of array or function type. The array-subscript

[]

and member-access

.

and

->

operators, the address

&

and indirection

*

unary operators, and pointer casts may

be used in the creation of an address constant, but the value of an object shall not be
accessed by use of these operators.

10

An implementation may accept other forms of constant expressions.

11

The semantic rules for the evaluation of a constant expression are the same as for
nonconstant expressions.

100)

Forward references: array declarators (6.7.5.2), initialization (6.7.8).

100) Thus, in the following initialization,

static int i = 2 || 1 / 0;

the expression is a valid integer constant expression with value one.

96 Language

§6.6

6.7 Declarations

Syntax

1

declaration:

declaration-specifiers init-declarator-list

opt

;

declaration-specifiers:

storage-class-specifier declaration-specifiers

opt

type-specifier declaration-specifiers

opt

type-qualifier declaration-specifiers

opt

function-specifier declaration-specifiers

opt

init-declarator-list:

init-declarator
init-declarator-list

,

init-declarator

init-declarator:

declarator
declarator

=

initializer

Constraints

2

A declaration shall declare at least a declarator (other than the parameters of a function or
the members of a structure or union), a tag, or the members of an enumeration.

3

If an identifier has no linkage, there shall be no more than one declaration of the identifier
(in a declarator or type specifier) with the same scope and in the same name space, except
for tags as specified in 6.7.2.3.

4

All declarations in the same scope that refer to the same object or function shall specify
compatible types.

Semantics

5

A declaration specifies the interpretation and attributes of a set of identifiers. A definition
of an identifier is a declaration for that identifier that:

— for an object, causes storage to be reserved for that object;

— for a function, includes the function body;

101)

— for an enumeration constant or typedef name, is the (only) declaration of the

identifier.

6

The declaration specifiers consist of a sequence of specifiers that indicate the linkage,
storage duration, and part of the type of the entities that the declarators denote. The init-
declarator-list is a comma-separated sequence of declarators, each of which may have

101) Function definitions have a different syntax, described in 6.9.1.

§6.7 Language

97

additional type information, or an initializer, or both. The declarators contain the
identifiers (if any) being declared.

7

If an identifier for an object is declared with no linkage, the type for the object shall be
complete by the end of its declarator, or by the end of its init-declarator if it has an
initializer; in the case of function parameters (including in prototypes), it is the adjusted
type (see 6.7.5.3) that is required to be complete.

Forward references: declarators (6.7.5), enumeration specifiers (6.7.2.2), initialization
(6.7.8).

6.7.1 Storage-class specifiers

Syntax

1

storage-class-specifier:

typedef

extern

static

auto

register

Constraints

2

At most, one storage-class specifier may be given in the declaration specifiers in a
declaration.

102)

Semantics

3

The

typedef

specifier is called a ‘‘storage-class specifier’’ for syntactic convenience

only; it is discussed in 6.7.7. The meanings of the various linkages and storage durations
were discussed in 6.2.2 and 6.2.4.

4

A declaration of an identifier for an object with storage-class specifier

register

suggests that access to the object be as fast as possible. The extent to which such
suggestions are effective is implementation-defined.

103)

5

The declaration of an identifier for a function that has block scope shall have no explicit
storage-class specifier other than

extern

.

102) See ‘‘future language directions’’ (6.11.5).

103) The implementation may treat any

register

declaration simply as an

auto

declaration. However,

whether or not addressable storage is actually used, the address of any part of an object declared with
storage-class specifier

register

cannot be computed, either explicitly (by use of the unary

&

operator as discussed in 6.5.3.2) or implicitly (by converting an array name to a pointer as discussed in
6.3.2.1). Thus, the only operator that can be applied to an array declared with storage-class specifier

register

is

sizeof

.

98 Language

§6.7.1

6

If an aggregate or union object is declared with a storage-class specifier other than

typedef

, the properties resulting from the storage-class specifier, except with respect to

linkage, also apply to the members of the object, and so on recursively for any aggregate
or union member objects.

Forward references: type definitions (6.7.7).

6.7.2 Type specifiers

Syntax

1

type-specifier:

void

char

short

int

long

float

double

signed

unsigned

_Bool

_Complex

struct-or-union-specifier

enum-specifier
typedef-name

Constraints

2

At least one type specifier shall be given in the declaration specifiers in each declaration,
and in the specifier-qualifier list in each struct declaration and type name. Each list of
type specifiers shall be one of the following sets (delimited by commas, when there is
more than one set on a line); the type specifiers may occur in any order, possibly
intermixed with the other declaration specifiers.

void

char

signed char

unsigned char

short

,

signed short

,

short int

, or

signed short int

unsigned short

, or

unsigned short int

int

,

signed

, or

signed int

§6.7.2 Language

99

unsigned

, or

unsigned int

long

,

signed long

,

long int

, or

signed long int

unsigned long

, or

unsigned long int

long long

,

signed long long

,

long long int

, or

signed long long int

unsigned long long

, or

unsigned long long int

float

double

long double

_Bool

float _Complex

double _Complex

long double _Complex

— struct or union specifier

— enum specifier

— typedef name

3

The type specifier

_Complex

shall not be used if the implementation does not provide

complex types.

104)

Semantics

4

Specifiers for structures, unions, and enumerations are discussed in 6.7.2.1 through
6.7.2.3. Declarations of typedef names are discussed in 6.7.7. The characteristics of the
other types are discussed in 6.2.5.

5

Each of the comma-separated sets designates the same type, except that for bit-fields, it is
implementation-defined whether the specifier

int

designates the same type as

signed

int

or the same type as

unsigned int

.

Forward references: enumeration specifiers (6.7.2.2), structure and union specifiers
(6.7.2.1), tags (6.7.2.3), type definitions (6.7.7).

104) Freestanding implementations are not required to provide complex types.

100 Language

§6.7.2

6.7.2.1 Structure and union specifiers

Syntax

1

struct-or-union-specifier:

struct-or-union identifier

opt

{

struct-declaration-list

}

struct-or-union identifier

struct-or-union:

struct

union

struct-declaration-list:

struct-declaration
struct-declaration-list struct-declaration

struct-declaration:

specifier-qualifier-list struct-declarator-list

;

specifier-qualifier-list:

type-specifier specifier-qualifier-list

opt

type-qualifier specifier-qualifier-list

opt

struct-declarator-list:

struct-declarator
struct-declarator-list

,

struct-declarator

struct-declarator:

declarator
declarator

opt

:

constant-expression

Constraints

2

A structure or union shall not contain a member with incomplete or function type (hence,
a structure shall not contain an instance of itself, but may contain a pointer to an instance
of itself), except that the last member of a structure with more than one named member
may have incomplete array type; such a structure (and any union containing, possibly
recursively, a member that is such a structure) shall not be a member of a structure or an
element of an array.

3

The expression that specifies the width of a bit-field shall be an integer constant
expression with a nonnegative value that does not exceed the width of an object of the
type that would be specified were the colon and expression omitted. If the value is zero,
the declaration shall have no declarator.

4

A bit-field shall have a type that is a qualified or unqualified version of

_Bool

,

signed

int

,

unsigned int

, or some other implementation-defined type.

§6.7.2.1 Language

101

Semantics

5

As discussed in 6.2.5, a structure is a type consisting of a sequence of members, whose
storage is allocated in an ordered sequence, and a union is a type consisting of a sequence
of members whose storage overlap.

6

Structure and union specifiers have the same form. The keywords

struct

and

union

indicate that the type being specified is, respectively, a structure type or a union type.

7

The presence of a struct-declaration-list in a struct-or-union-specifier declares a new type,
within a translation unit. The struct-declaration-list is a sequence of declarations for the
members of the structure or union. If the struct-declaration-list contains no named
members, the behavior is undefined. The type is incomplete until after the

}

that

terminates the list.

8

A member of a structure or union may have any object type other than a variably
modified type.

105)

In addition, a member may be declared to consist of a specified

number of bits (including a sign bit, if any). Such a member is called a bit-field;

106)

its

width is preceded by a colon.

9

A bit-field is interpreted as a signed or unsigned integer type consisting of the specified
number of bits.

107)

If the value 0 or 1 is stored into a nonzero-width bit-field of type

_Bool

, the value of the bit-field shall compare equal to the value stored.

10

An implementation may allocate any addressable storage unit large enough to hold a bit-
field. If enough space remains, a bit-field that immediately follows another bit-field in a
structure shall be packed into adjacent bits of the same unit. If insufficient space remains,
whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is
implementation-defined. The order of allocation of bit-fields within a unit (high-order to
low-order or low-order to high-order) is implementation-defined. The alignment of the
addressable storage unit is unspecified.

11

A bit-field declaration with no declarator, but only a colon and a width, indicates an
unnamed bit-field.

108)

As a special case, a bit-field structure member with a width of 0

indicates that no further bit-field is to be packed into the unit in which the previous bit-
field, if any, was placed.

105) A structure or union can not contain a member with a variably modified type because member names

are not ordinary identifiers as defined in 6.2.3.

106) The unary

&

(address-of) operator cannot be applied to a bit-field object; thus, there are no pointers to

or arrays of bit-field objects.

107) As specified in 6.7.2 above, if the actual type specifier used is

int

or a typedef-name defined as

int

,

then it is implementation-defined whether the bit-field is signed or unsigned.

108) An unnamed bit-field structure member is useful for padding to conform to externally imposed

layouts.

102 Language

§6.7.2.1

12

Each non-bit-field member of a structure or union object is aligned in an implementation-
defined manner appropriate to its type.

13

Within a structure object, the non-bit-field members and the units in which bit-fields
reside have addresses that increase in the order in which they are declared. A pointer to a
structure object, suitably converted, points to its initial member (or if that member is a
bit-field, then to the unit in which it resides), and vice versa. There may be unnamed
padding within a structure object, but not at its beginning.

14

The size of a union is sufficient to contain the largest of its members. The value of at
most one of the members can be stored in a union object at any time. A pointer to a
union object, suitably converted, points to each of its members (or if a member is a bit-
field, then to the unit in which it resides), and vice versa.

15

There may be unnamed padding at the end of a structure or union.

16

As a special case, the last element of a structure with more than one named member may
have an incomplete array type; this is called a flexible array member. In most situations,
the flexible array member is ignored. In particular, the size of the structure is as if the
flexible array member were omitted except that it may have more trailing padding than
the omission would imply. Howev er, when a

.

(or

->

) operator has a left operand that is

(a pointer to) a structure with a flexible array member and the right operand names that
member, it behaves as if that member were replaced with the longest array (with the same
element type) that would not make the structure larger than the object being accessed; the
offset of the array shall remain that of the flexible array member, even if this would differ
from that of the replacement array. If this array would have no elements, it behaves as if
it had one element but the behavior is undefined if any attempt is made to access that
element or to generate a pointer one past it.

17

EXAMPLE After the declaration:

struct s { int n; double d[]; };

the structure

struct s

has a flexible array member

d

. A typical way to use this is:

int m = /*

some value

*/;

struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));

and assuming that the call to

malloc

succeeds, the object pointed to by

p

behaves, for most purposes, as if

p

had been declared as:

struct { int n; double d[m]; } *p;

(there are circumstances in which this equivalence is broken; in particular, the offsets of member

d

might

not be the same).

18

Following the above declaration:

§6.7.2.1 Language

103

struct s t1 = { 0 };

//

valid

struct s t2 = { 1, { 4.2 }};

//

invalid

t1.n = 4;

//

valid

t1.d[0] = 4.2;

//

might be undefined behavior

The initialization of

t2

is invalid (and violates a constraint) because

struct s

is treated as if it did not

contain member

d

. The assignment to

t1.d[0]

is probably undefined behavior, but it is possible that

sizeof (struct s) >= offsetof(struct s, d) + sizeof (double)

in which case the assignment would be legitimate. Nevertheless, it cannot appear in strictly conforming
code.

19

After the further declaration:

struct ss { int n; };

the expressions:

sizeof (struct s) >= sizeof (struct ss)

sizeof (struct s) >= offsetof(struct s, d)

are always equal to 1.

20

If

sizeof (double)

is 8, then after the following code is executed:

struct s *s1;

struct s *s2;

s1 = malloc(sizeof (struct s) + 64);

s2 = malloc(sizeof (struct s) + 46);

and assuming that the calls to

malloc

succeed, the objects pointed to by

s1

and

s2

behave, for most

purposes, as if the identifiers had been declared as:

struct { int n; double d[8]; } *s1;

struct { int n; double d[5]; } *s2;

21

Following the further successful assignments:

s1 = malloc(sizeof (struct s) + 10);

s2 = malloc(sizeof (struct s) +

6);

they then behave as if the declarations were:

struct { int n; double d[1]; } *s1, *s2;

and:

double *dp;

dp = &(s1->d[0]); //

valid

*dp = 42;

//

valid

dp = &(s2->d[0]); //

valid

*dp = 42;

//

undefined behavior

22

The assignment:

*s1 = *s2;

only copies the member

n

; if any of the array elements are within the first

sizeof (struct s)

bytes

of the structure, they might be copied or simply overwritten with indeterminate values.

Forward references: tags (6.7.2.3).

104 Language

§6.7.2.1

6.7.2.2 Enumeration specifiers

Syntax

1

enum-specifier:

enum

identifier

opt

{

enumerator-list

}

enum

identifier

opt

{

enumerator-list

, }

enum

identifier

enumerator-list:

enumerator
enumerator-list

,

enumerator

enumerator:

enumeration-constant
enumeration-constant

=

constant-expression

Constraints

2

The expression that defines the value of an enumeration constant shall be an integer
constant expression that has a value representable as an

int

.

Semantics

3

The identifiers in an enumerator list are declared as constants that have type

int

and

may appear wherever such are permitted.

109)

An enumerator with

=

defines its

enumeration constant as the value of the constant expression. If the first enumerator has
no

=

, the value of its enumeration constant is 0. Each subsequent enumerator with no

=

defines its enumeration constant as the value of the constant expression obtained by
adding 1 to the value of the previous enumeration constant. (The use of enumerators with

=

may produce enumeration constants with values that duplicate other values in the same

enumeration.) The enumerators of an enumeration are also known as its members.

4

Each enumerated type shall be compatible with

char

, a signed integer type, or an

unsigned integer type. The choice of type is implementation-defined,

110)

but shall be

capable of representing the values of all the members of the enumeration. The
enumerated type is incomplete until after the

}

that terminates the list of enumerator

declarations.

109) Thus, the identifiers of enumeration constants declared in the same scope shall all be distinct from

each other and from other identifiers declared in ordinary declarators.

110) An implementation may delay the choice of which integer type until all enumeration constants have

been seen.

§6.7.2.2 Language

105

5

EXAMPLE The following fragment:

enum hue { chartreuse, burgundy, claret=20, winedark };

enum hue col, *cp;

col = claret;

cp = &col;

if (*cp != burgundy)

/*

...

*/

makes

hue

the tag of an enumeration, and then declares

col

as an object that has that type and

cp

as a

pointer to an object that has that type. The enumerated values are in the set { 0, 1, 20, 21 }.

Forward references: tags (6.7.2.3).

6.7.2.3 Tags

Constraints

1

A specific type shall have its content defined at most once.

2

Where two declarations that use the same tag declare the same type, they shall both use
the same choice of

struct

,

union

, or

enum

.

3

A type specifier of the form

enum

identifier

without an enumerator list shall only appear after the type it specifies is complete.

Semantics

4

All declarations of structure, union, or enumerated types that have the same scope and
use the same tag declare the same type. The type is incomplete

111)

until the closing brace

of the list defining the content, and complete thereafter.

5

Tw o declarations of structure, union, or enumerated types which are in different scopes or
use different tags declare distinct types. Each declaration of a structure, union, or
enumerated type which does not include a tag declares a distinct type.

6

A type specifier of the form

struct-or-union identifier

opt

{

struct-declaration-list

}

or

enum

identifier

{

enumerator-list

}

or

enum

identifier

{

enumerator-list

, }

declares a structure, union, or enumerated type. The list defines the structure content,

111) An incomplete type may only by used when the size of an object of that type is not needed. It is not

needed, for example, when a typedef name is declared to be a specifier for a structure or union, or
when a pointer to or a function returning a structure or union is being declared. (See incomplete types
in 6.2.5.) The specification has to be complete before such a function is called or defined.

106 Language

§6.7.2.3

union content, or enumeration content. If an identifier is provided,

112)

the type specifier

also declares the identifier to be the tag of that type.

7

A declaration of the form

struct-or-union identifier

;

specifies a structure or union type and declares the identifier as a tag of that type.

113)

8

If a type specifier of the form

struct-or-union identifier

occurs other than as part of one of the above forms, and no other declaration of the
identifier as a tag is visible, then it declares an incomplete structure or union type, and
declares the identifier as the tag of that type.

113)

9

If a type specifier of the form

struct-or-union identifier

or

enum

identifier

occurs other than as part of one of the above forms, and a declaration of the identifier as a
tag is visible, then it specifies the same type as that other declaration, and does not
redeclare the tag.

10

EXAMPLE 1

This mechanism allows declaration of a self-referential structure.

struct tnode {

int count;

struct tnode *left, *right;

};

specifies a structure that contains an integer and two pointers to objects of the same type. Once this
declaration has been given, the declaration

struct tnode s, *sp;

declares

s

to be an object of the given type and

sp

to be a pointer to an object of the given type. With

these declarations, the expression

sp->left

refers to the left

struct tnode

pointer of the object to

which

sp

points; the expression

s.right->count

designates the

count

member of the right

struct

tnode

pointed to from

s

.

11

The following alternative formulation uses the

typedef

mechanism:

112) If there is no identifier, the type can, within the translation unit, only be referred to by the declaration

of which it is a part. Of course, when the declaration is of a typedef name, subsequent declarations
can make use of that typedef name to declare objects having the specified structure, union, or
enumerated type.

113) A similar construction with

enum

does not exist.

§6.7.2.3 Language

107

typedef struct tnode TNODE;

struct tnode {

int count;

TNODE *left, *right;

};

TNODE s, *sp;

12

EXAMPLE 2

To illustrate the use of prior declaration of a tag to specify a pair of mutually referential

structures, the declarations

struct s1 { struct s2 *s2p; /*

...

*/ }; // D1

struct s2 { struct s1 *s1p; /*

...

*/ }; // D2

specify a pair of structures that contain pointers to each other. Note, however, that if

s2

were already

declared as a tag in an enclosing scope, the declaration

D1

would refer to it, not to the tag

s2

declared in

D2

. To eliminate this context sensitivity, the declaration

struct s2;

may be inserted ahead of

D1

. This declares a new tag

s2

in the inner scope; the declaration

D2

then

completes the specification of the new type.

Forward references: declarators (6.7.5), array declarators (6.7.5.2), type definitions
(6.7.7).

6.7.3 Type qualifiers

Syntax

1

type-qualifier:

const

restrict

volatile

Constraints

2

Types other than pointer types derived from object or incomplete types shall not be
restrict-qualified.

Semantics

3

The properties associated with qualified types are meaningful only for expressions that
are lvalues.

114)

4

If the same qualifier appears more than once in the same specifier-qualifier-list, either
directly or via one or more

typedef

s, the behavior is the same as if it appeared only

once.

114) The implementation may place a

const

object that is not

volatile

in a read-only region of

storage. Moreover, the implementation need not allocate storage for such an object if its address is
never used.

108 Language

§6.7.3

5

If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is
made to refer to an object defined with a volatile-qualified type through use of an lvalue
with non-volatile-qualified type, the behavior is undefined.

115)

6

An object that has volatile-qualified type may be modified in ways unknown to the
implementation or have other unknown side effects. Therefore any expression referring
to such an object shall be evaluated strictly according to the rules of the abstract machine,
as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the
object shall agree with that prescribed by the abstract machine, except as modified by the
unknown factors mentioned previously.

116)

What constitutes an access to an object that

has volatile-qualified type is implementation-defined.

7

An object that is accessed through a restrict-qualified pointer has a special association
with that pointer. This association, defined in 6.7.3.1 below, requires that all accesses to
that object use, directly or indirectly, the value of that particular pointer.

117)

The intended

use of the

restrict

qualifier (like the

register

storage class) is to promote

optimization, and deleting all instances of the qualifier from all preprocessing translation
units composing a conforming program does not change its meaning (i.e., observable
behavior).

8

If the specification of an array type includes any type qualifiers, the element type is so-
qualified, not the array type. If the specification of a function type includes any type
qualifiers, the behavior is undefined.

118)

9

For two qualified types to be compatible, both shall have the identically qualified version
of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers
does not affect the specified type.

10

EXAMPLE 1

An object declared

extern const volatile int real_time_clock;

may be modifiable by hardware, but cannot be assigned to, incremented, or decremented.

115) This applies to those objects that behave as if they were defined with qualified types, even if they are

never actually defined as objects in the program (such as an object at a memory-mapped input/output
address).

116) A

volatile

declaration may be used to describe an object corresponding to a memory-mapped

input/output port or an object accessed by an asynchronously interrupting function. Actions on
objects so declared shall not be ‘‘optimized out’’ by an implementation or reordered except as
permitted by the rules for evaluating expressions.

117) For example, a statement that assigns a value returned by

malloc

to a single pointer establishes this

association between the allocated object and the pointer.

118) Both of these can occur through the use of

typedef

s.

§6.7.3 Language

109

11

EXAMPLE 2

The following declarations and expressions illustrate the behavior when type qualifiers

modify an aggregate type:

const struct s { int mem; } cs = { 1 };

struct s ncs;

//

the object

ncs

is modifiable

typedef int A[2][3];

const A a = {{4, 5, 6}, {7, 8, 9}}; //

array of array of

const int

int *pi;

const int *pci;

ncs = cs;

//

valid

cs = ncs;

//

violates modifiable lvalue constraint for

=

pi = &ncs.mem; //

valid

pi = &cs.mem;

//

violates type constraints for

=

pci = &cs.mem; //

valid

pi = a[0];

//

invalid:

a[0]

has type ‘‘

const int *

’’

6.7.3.1 Formal definition of

restrict

1

Let

D

be a declaration of an ordinary identifier that provides a means of designating an

object

P

as a restrict-qualified pointer to type

T

.

2

If

D

appears inside a block and does not have storage class

extern

, let

B

denote the

block. If

D

appears in the list of parameter declarations of a function definition, let

B

denote the associated block. Otherwise, let

B

denote the block of

main

(or the block of

whatever function is called at program startup in a freestanding environment).

3

In what follows, a pointer expression

E

is said to be based on object

P

if (at some

sequence point in the execution of

B

prior to the evaluation of

E

) modifying

P

to point to

a copy of the array object into which it formerly pointed would change the value of

E

.

119)

Note that ‘‘based’’ is defined only for expressions with pointer types.

4

During each execution of

B

, let

L

be any lvalue that has

&L

based on

P

. If

L

is used to

access the value of the object

X

that it designates, and

X

is also modified (by any means),

then the following requirements apply:

T

shall not be const-qualified. Every other lvalue

used to access the value of

X

shall also have its address based on

P

. Every access that

modifies

X

shall be considered also to modify

P

, for the purposes of this subclause. If

P

is assigned the value of a pointer expression

E

that is based on another restricted pointer

object

P2

, associated with block

B2

, then either the execution of

B2

shall begin before

the execution of

B

, or the execution of

B2

shall end prior to the assignment. If these

requirements are not met, then the behavior is undefined.

5

Here an execution of

B

means that portion of the execution of the program that would

correspond to the lifetime of an object with scalar type and automatic storage duration

119) In other words,

E

depends on the value of

P

itself rather than on the value of an object referenced

indirectly through

P

. For example, if identifier

p

has type

(int **restrict)

, then the pointer

expressions

p

and

p+1

are based on the restricted pointer object designated by

p

, but the pointer

expressions

*p

and

p[1]

are not.

110 Language

§6.7.3.1

associated with

B

.

6

A translator is free to ignore any or all aliasing implications of uses of

restrict

.

7

EXAMPLE 1

The file scope declarations

int * restrict a;

int * restrict b;

extern int c[];

assert that if an object is accessed using one of

a

,

b

, or

c

, and that object is modified anywhere in the

program, then it is never accessed using either of the other two.

8

EXAMPLE 2

The function parameter declarations in the following example

void f(int n, int * restrict p, int * restrict q)

{

while (n-- > 0)

*p++ = *q++;

}

assert that, during each execution of the function, if an object is accessed through one of the pointer
parameters, then it is not also accessed through the other.

9

The benefit of the

restrict

qualifiers is that they enable a translator to make an effective dependence

analysis of function

f

without examining any of the calls of

f

in the program. The cost is that the

programmer has to examine all of those calls to ensure that none give undefined behavior. For example, the
second call of

f

in

g

has undefined behavior because each of

d[1]

through

d[49]

is accessed through

both

p

and

q

.

void g(void)

{

extern int d[100];

f(50, d + 50, d); //

valid

f(50, d +

1, d); //

undefined behavior

}

10

EXAMPLE 3

The function parameter declarations

void h(int n, int * restrict p, int * restrict q, int * restrict r)

{

int i;

for (i = 0; i < n; i++)

p[i] = q[i] + r[i];

}

illustrate how an unmodified object can be aliased through two restricted pointers. In particular, if

a

and

b

are disjoint arrays, a call of the form

h(100, a, b, b)

has defined behavior, because array

b

is not

modified within function

h

.

11

EXAMPLE 4

The rule limiting assignments between restricted pointers does not distinguish between a

function call and an equivalent nested block. With one exception, only ‘‘outer-to-inner’’ assignments
between restricted pointers declared in nested blocks have defined behavior.

§6.7.3.1 Language

111

{

int * restrict p1;

int * restrict q1;

p1 = q1; //

undefined behavior

{

int * restrict p2 = p1; //

valid

int * restrict q2 = q1; //

valid

p1 = q2;

//

undefined behavior

p2 = q2;

//

undefined behavior

}

}

12

The one exception allows the value of a restricted pointer to be carried out of the block in which it (or, more
precisely, the ordinary identifier used to designate it) is declared when that block finishes execution. For
example, this permits

new_vector

to return a

vector

.

typedef struct { int n; float * restrict v; } vector;

vector new_vector(int n)

{

vector t;

t.n = n;

t.v = malloc(n * sizeof (float));

return t;

}

6.7.4 Function specifiers

Syntax

1

function-specifier:

inline

Constraints

2

Function specifiers shall be used only in the declaration of an identifier for a function.

3

An inline definition of a function with external linkage shall not contain a definition of a
modifiable object with static storage duration, and shall not contain a reference to an
identifier with internal linkage.

4

In a hosted environment, the

inline

function specifier shall not appear in a declaration

of

main

.

Semantics

5

A function declared with an

inline

function specifier is an inline function. The

function specifier may appear more than once; the behavior is the same as if it appeared
only once. Making a function an inline function suggests that calls to the function be as
fast as possible.

120)

The extent to which such suggestions are effective is

implementation-defined.

121)

6

Any function with internal linkage can be an inline function. For a function with external
linkage, the following restrictions apply: If a function is declared with an

inline

112 Language

§6.7.4

function specifier, then it shall also be defined in the same translation unit. If all of the
file scope declarations for a function in a translation unit include the

inline

function

specifier without

extern

, then the definition in that translation unit is an inline

definition. An inline definition does not provide an external definition for the function,
and does not forbid an external definition in another translation unit. An inline definition
provides an alternative to an external definition, which a translator may use to implement
any call to the function in the same translation unit. It is unspecified whether a call to the
function uses the inline definition or the external definition.

122)

7

EXAMPLE The declaration of an inline function with external linkage can result in either an external
definition, or a definition available for use only within the translation unit. A file scope declaration with

extern

creates an external definition. The following example shows an entire translation unit.

inline double fahr(double t)

{

return (9.0 * t) / 5.0 + 32.0;

}

inline double cels(double t)

{

return (5.0 * (t - 32.0)) / 9.0;

}

extern double fahr(double);

//

creates an external definition

double convert(int is_fahr, double temp)

{

/*

A translator may perform inline substitutions

*/

return is_fahr ? cels(temp) : fahr(temp);

}

8

Note that the definition of

fahr

is an external definition because

fahr

is also declared with

extern

, but

the definition of

cels

is an inline definition. Because

cels

has external linkage and is referenced, an

external definition has to appear in another translation unit (see 6.9); the inline definition and the external
definition are distinct and either may be used for the call.

Forward references: function definitions (6.9.1).

120) By using, for example, an alternative to the usual function call mechanism, such as ‘‘inline

substitution’’. Inline substitution is not textual substitution, nor does it create a new function.
Therefore, for example, the expansion of a macro used within the body of the function uses the
definition it had at the point the function body appears, and not where the function is called; and
identifiers refer to the declarations in scope where the body occurs. Likewise, the function has a
single address, regardless of the number of inline definitions that occur in addition to the external
definition.

121) For example, an implementation might never perform inline substitution, or might only perform inline

substitutions to calls in the scope of an

inline

declaration.

122) Since an inline definition is distinct from the corresponding external definition and from any other

corresponding inline definitions in other translation units, all corresponding objects with static storage
duration are also distinct in each of the definitions.

§6.7.4 Language

113

6.7.5 Declarators

Syntax

1

declarator:

pointer

opt

direct-declarator

direct-declarator:

identifier

(

declarator

)

direct-declarator

[

type-qualifier-list

opt

assignment-expression

opt

]

direct-declarator

[ static

type-qualifier-list

opt

assignment-expression

]

direct-declarator

[

type-qualifier-list

static

assignment-expression

]

direct-declarator

[

type-qualifier-list

opt

* ]

direct-declarator

(

parameter-type-list

)

direct-declarator

(

identifier-list

opt

)

pointer:

*

type-qualifier-list

opt

*

type-qualifier-list

opt

pointer

type-qualifier-list:

type-qualifier
type-qualifier-list type-qualifier

parameter-type-list:

parameter-list
parameter-list

, ...

parameter-list:

parameter-declaration
parameter-list

,

parameter-declaration

parameter-declaration:

declaration-specifiers declarator
declaration-specifiers abstract-declarator

opt

identifier-list:

identifier
identifier-list

,

identifier

Semantics

2

Each declarator declares one identifier, and asserts that when an operand of the same
form as the declarator appears in an expression, it designates a function or object with the
scope, storage duration, and type indicated by the declaration specifiers.

3

full declarator is a declarator that is not part of another declarator. The end of a full
declarator is a sequence point. If, in the nested sequence of declarators in a full
114 Language

§6.7.5

declarator, there is a declarator specifying a variable length array type, the type specified
by the full declarator is said to be variably modified. Furthermore, any type derived by
declarator type derivation from a variably modified type is itself variably modified.

4

In the following subclauses, consider a declaration

T D1

where

T

contains the declaration specifiers that specify a type (such as

int

) and

D1

is

a declarator that contains an identifier ident. The type specified for the identifier ident in
the various forms of declarator is described inductively using this notation.

5

If, in the declaration ‘‘

T D1

’’,

D1

has the form

identifier

then the type specified for ident is .

6

If, in the declaration ‘‘

T D1

’’,

D1

has the form

( D )

then ident has the type specified by the declaration ‘‘

T D

’’. Thus, a declarator in

parentheses is identical to the unparenthesized declarator, but the binding of complicated
declarators may be altered by parentheses.

Implementation limits

7

As discussed in 5.2.4.1, an implementation may limit the number of pointer, array, and
function declarators that modify an arithmetic, structure, union, or incomplete type, either
directly or via one or more

typedef

s.

Forward references: array declarators (6.7.5.2), type definitions (6.7.7).

6.7.5.1 Pointer declarators

Semantics

1

If, in the declaration ‘‘

T D1

’’,

D1

has the form

*

type-qualifier-list

opt

D

and the type specified for ident in the declaration ‘‘

T D

’’ is ‘‘derived-declarator-type-list

’’, then the type specified for ident is ‘‘derived-declarator-type-list type-qualifier-list
pointer to ’’. For each type qualifier in the list, ident is a so-qualified pointer.

2

For two pointer types to be compatible, both shall be identically qualified and both shall
be pointers to compatible types.

3

EXAMPLE The following pair of declarations demonstrates the difference between a ‘‘variable pointer
to a constant value’’ and a ‘‘constant pointer to a variable value’’.

§6.7.5.1 Language

115

const int *ptr_to_constant;

int *const constant_ptr;

The contents of any object pointed to by

ptr_to_constant

shall not be modified through that pointer,

but

ptr_to_constant

itself may be changed to point to another object. Similarly, the contents of the

int

pointed to by

constant_ptr

may be modified, but

constant_ptr

itself shall always point to the

same location.

4

The declaration of the constant pointer

constant_ptr

may be clarified by including a definition for the

type ‘‘pointer to

int

’’.

typedef int *int_ptr;

const int_ptr constant_ptr;

declares

constant_ptr

as an object that has type ‘‘const-qualified pointer to

int

’’.

6.7.5.2 Array declarators

Constraints

1

In addition to optional type qualifiers and the keyword

static

, the

[

and

]

may delimit

an expression or

*

. If they delimit an expression (which specifies the size of an array), the

expression shall have an integer type. If the expression is a constant expression, it shall
have a value greater than zero. The element type shall not be an incomplete or function
type. The optional type qualifiers and the keyword

static

shall appear only in a

declaration of a function parameter with an array type, and then only in the outermost
array type derivation.

2

An ordinary identifier (as defined in 6.2.3) that has a variably modified type shall have
either block scope and no linkage or function prototype scope. If an identifier is declared
to be an object with static storage duration, it shall not have a variable length array type.

Semantics

3

If, in the declaration ‘‘

T D1

’’,

D1

has one of the forms:

D[

type-qualifier-list

opt

assignment-expression

opt

]

D[ static

type-qualifier-list

opt

assignment-expression

]

D[

type-qualifier-list

static

assignment-expression

]

D[

type-qualifier-list

opt

* ]

and the type specified for ident in the declaration ‘‘

T D

’’ is ‘‘derived-declarator-type-list

’’, then the type specified for ident is ‘‘derived-declarator-type-list array of ’’.

123)

(See 6.7.5.3 for the meaning of the optional type qualifiers and the keyword

static

.)

4

If the size is not present, the array type is an incomplete type. If the size is

*

instead of

being an expression, the array type is a variable length array type of unspecified size,
which can only be used in declarations with function prototype scope;

124)

such arrays are

nonetheless complete types. If the size is an integer constant expression and the element

123) When several ‘‘array of’’ specifications are adjacent, a multidimensional array is declared.

116 Language

§6.7.5.2

type has a known constant size, the array type is not a variable length array type;
otherwise, the array type is a variable length array type.

5

If the size is an expression that is not an integer constant expression: if it occurs in a
declaration at function prototype scope, it is treated as if it were replaced by

*

; otherwise,

each time it is evaluated it shall have a value greater than zero. The size of each instance
of a variable length array type does not change during its lifetime. Where a size
expression is part of the operand of a

sizeof

operator and changing the value of the

size expression would not affect the result of the operator, it is unspecified whether or not
the size expression is evaluated.

6

For two array types to be compatible, both shall have compatible element types, and if
both size specifiers are present, and are integer constant expressions, then both size
specifiers shall have the same constant value. If the two array types are used in a context
which requires them to be compatible, it is undefined behavior if the two size specifiers
evaluate to unequal values.

7

EXAMPLE 1

float fa[11], *afp[17];

declares an array of

float

numbers and an array of pointers to

float

numbers.

8

EXAMPLE 2

Note the distinction between the declarations

extern int *x;

extern int y[];

The first declares

x

to be a pointer to

int

; the second declares

y

to be an array of

int

of unspecified size

(an incomplete type), the storage for which is defined elsewhere.

9

EXAMPLE 3

The following declarations demonstrate the compatibility rules for variably modified types.

extern int n;

extern int m;

void fcompat(void)

{

int a[n][6][m];

int (*p)[4][n+1];

int c[n][n][6][m];

int (*r)[n][n][n+1];

p = a;

//

invalid: not compatible because

4 != 6

r = c;

//

compatible, but defined behavior only if

// n == 6

and

m == n+1

}

124) Thus,

*

can be used only in function declarations that are not definitions (see 6.7.5.3).

§6.7.5.2 Language

117

10

EXAMPLE 4

All declarations of variably modified (VM) types have to be at either block scope or

function prototype scope. Array objects declared with the

static

or

extern

storage-class specifier

cannot have a variable length array (VLA) type. However, an object declared with the

static

storage-

class specifier can have a VM type (that is, a pointer to a VLA type). Finally, all identifiers declared with a
VM type have to be ordinary identifiers and cannot, therefore, be members of structures or unions.

extern int n;

int A[n];

//

invalid: file scope VLA

extern int (*p2)[n];

//

invalid: file scope VM

int B[100];

//

valid: file scope but not VM

void fvla(int m, int C[m][m]);

//

valid: VLA with prototype scope

void fvla(int m, int C[m][m])

//

valid: adjusted to auto pointer to VLA

{

typedef int VLA[m][m];

//

valid: block scope typedef VLA

struct tag {

int (*y)[n];

//

invalid:

y

not ordinary identifier

int z[n];

//

invalid:

z

not ordinary identifier

};

int D[m];

//

valid: auto VLA

static int E[m];

//

invalid: static block scope VLA

extern int F[m];

//

invalid:

F

has linkage and is VLA

int (*s)[m];

//

valid: auto pointer to VLA

extern int (*r)[m];

//

invalid:

r

has linkage and points to VLA

static int (*q)[m] = &B;

//

valid:

q

is a static block pointer to VLA

}

Forward references:

function declarators (6.7.5.3), function definitions (6.9.1),

initialization (6.7.8).

6.7.5.3 Function declarators (including prototypes)

Constraints

1

A function declarator shall not specify a return type that is a function type or an array
type.

2

The only storage-class specifier that shall occur in a parameter declaration is

register

.

3

An identifier list in a function declarator that is not part of a definition of that function
shall be empty.

4

After adjustment, the parameters in a parameter type list in a function declarator that is
part of a definition of that function shall not have incomplete type.

Semantics

5

If, in the declaration ‘‘

T D1

’’,

D1

has the form

D(

parameter-type-list

)

or

D(

identifier-list

opt

)

118 Language

§6.7.5.3

and the type specified for ident in the declaration ‘‘

T D

’’ is ‘‘derived-declarator-type-list

’’, then the type specified for ident is ‘‘derived-declarator-type-list function returning
’’.

6

A parameter type list specifies the types of, and may declare identifiers for, the
parameters of the function.

7

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the

[

and

]

of the

array type derivation. If the keyword

static

also appears within the

[

and

]

of the

array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.

8

A declaration of a parameter as ‘‘function returning type’’ shall be adjusted to ‘‘pointer to
function returning type’’, as in 6.3.2.1.

9

If the list terminates with an ellipsis (

, ...

), no information about the number or types

of the parameters after the comma is supplied.

125)

10

The special case of an unnamed parameter of type

void

as the only item in the list

specifies that the function has no parameters.

11

If, in a parameter declaration, an identifier can be treated either as a typedef name or as a
parameter name, it shall be taken as a typedef name.

12

If the function declarator is not part of a definition of that function, parameters may have
incomplete type and may use the

[*]

notation in their sequences of declarator specifiers

to specify variable length array types.

13

The storage-class specifier in the declaration specifiers for a parameter declaration, if
present, is ignored unless the declared parameter is one of the members of the parameter
type list for a function definition.

14

An identifier list declares only the identifiers of the parameters of the function. An empty
list in a function declarator that is part of a definition of that function specifies that the
function has no parameters. The empty list in a function declarator that is not part of a
definition of that function specifies that no information about the number or types of the
parameters is supplied.

126)

15

For two function types to be compatible, both shall specify compatible return types.

127)

125) The macros defined in the

<stdarg.h>

header (7.15) may be used to access arguments that

correspond to the ellipsis.

126) See ‘‘future language directions’’ (6.11.6).

127) If both function types are ‘‘old style’’, parameter types are not compared.

§6.7.5.3 Language

119

Moreover, the parameter type lists, if both are present, shall agree in the number of
parameters and in use of the ellipsis terminator; corresponding parameters shall have
compatible types. If one type has a parameter type list and the other type is specified by a
function declarator that is not part of a function definition and that contains an empty
identifier list, the parameter list shall not have an ellipsis terminator and the type of each
parameter shall be compatible with the type that results from the application of the
default argument promotions. If one type has a parameter type list and the other type is
specified by a function definition that contains a (possibly empty) identifier list, both shall
agree in the number of parameters, and the type of each prototype parameter shall be
compatible with the type that results from the application of the default argument
promotions to the type of the corresponding identifier. (In the determination of type
compatibility and of a composite type, each parameter declared with function or array
type is taken as having the adjusted type and each parameter declared with qualified type
is taken as having the unqualified version of its declared type.)

16

EXAMPLE 1

The declaration

int f(void), *fip(), (*pfi)();

declares a function

f

with no parameters returning an

int

, a function

fip

with no parameter specification

returning a pointer to an

int

, and a pointer

pfi

to a function with no parameter specification returning an

int

. It is especially useful to compare the last two. The binding of

*fip()

is

*(fip())

, so that the

declaration suggests, and the same construction in an expression requires, the calling of a function

fip

,

and then using indirection through the pointer result to yield an

int

. In the declarator

(*pfi)()

, the

extra parentheses are necessary to indicate that indirection through a pointer to a function yields a function
designator, which is then used to call the function; it returns an

int

.

17

If the declaration occurs outside of any function, the identifiers have file scope and external linkage. If the
declaration occurs inside a function, the identifiers of the functions

f

and

fip

have block scope and either

internal or external linkage (depending on what file scope declarations for these identifiers are visible), and
the identifier of the pointer

pfi

has block scope and no linkage.

18

EXAMPLE 2

The declaration

int (*apfi[3])(int *x, int *y);

declares an array

apfi

of three pointers to functions returning

int

. Each of these functions has two

parameters that are pointers to

int

. The identifiers

x

and

y

are declared for descriptive purposes only and

go out of scope at the end of the declaration of

apfi

.

19

EXAMPLE 3

The declaration

int (*fpfi(int (*)(long), int))(int, ...);

declares a function

fpfi

that returns a pointer to a function returning an

int

. The function

fpfi

has two

parameters: a pointer to a function returning an

int

(with one parameter of type

long int

), and an

int

.

The pointer returned by

fpfi

points to a function that has one

int

parameter and accepts zero or more

additional arguments of any type.

120 Language

§6.7.5.3

20

EXAMPLE 4

The following prototype has a variably modified parameter.

void addscalar(int n, int m,

double a[n][n*m+300], double x);

int main()

{

double b[4][308];

addscalar(4, 2, b, 2.17);

return 0;

}

void addscalar(int n, int m,

double a[n][n*m+300], double x)

{

for (int i = 0; i < n; i++)

for (int j = 0, k = n*m+300; j < k; j++)

// a

is a pointer to a VLA with

n*m+300

elements

a[i][j] += x;

}

21

EXAMPLE 5

The following are all compatible function prototype declarators.

double maximum(int n, int m, double a[n][m]);

double maximum(int n, int m, double a[*][*]);

double maximum(int n, int m, double a[ ][*]);

double maximum(int n, int m, double a[ ][m]);

as are:

void f(double (* restrict a)[5]);

void f(double a[restrict][5]);

void f(double a[restrict 3][5]);

void f(double a[restrict static 3][5]);

(Note that the last declaration also specifies that the argument corresponding to

a

in any call to

f

must be a

non-null pointer to the first of at least three arrays of 5 doubles, which the others do not.)

Forward references: function definitions (6.9.1), type names (6.7.6).

§6.7.5.3 Language

121

6.7.6 Type names

Syntax

1

type-name:

specifier-qualifier-list abstract-declarator

opt

abstract-declarator:

pointer
pointer

opt

direct-abstract-declarator

direct-abstract-declarator:

(

abstract-declarator

)

direct-abstract-declarator

opt

[

type-qualifier-list

opt

assignment-expression

opt

]

direct-abstract-declarator

opt

[ static

type-qualifier-list

opt

assignment-expression

]

direct-abstract-declarator

opt

[

type-qualifier-list

static

assignment-expression

]

direct-abstract-declarator

opt

[ * ]

direct-abstract-declarator

opt

(

parameter-type-list

opt

)

Semantics

2

In several contexts, it is necessary to specify a type. This is accomplished using a type
name
, which is syntactically a declaration for a function or an object of that type that
omits the identifier.

128)

3

EXAMPLE The constructions

(a)

int

(b)

int *

(c)

int *[3]

(d)

int (*)[3]

(e)

int (*)[*]

(f)

int *()

(g)

int (*)(void)

(h)

int (*const [])(unsigned int, ...)

name respectively the types (a)

int

, (b) pointer to

int

, (c) array of three pointers to

int

, (d) pointer to an

array of three

int

s, (e) pointer to a variable length array of an unspecified number of

int

s, (f) function

with no parameter specification returning a pointer to

int

, (g) pointer to function with no parameters

returning an

int

, and (h) array of an unspecified number of constant pointers to functions, each with one

parameter that has type

unsigned int

and an unspecified number of other parameters, returning an

int

.

128) As indicated by the syntax, empty parentheses in a type name are interpreted as ‘‘function with no

parameter specification’’, rather than redundant parentheses around the omitted identifier.

122 Language

§6.7.6

6.7.7 Type definitions

Syntax

1

typedef-name:

identifier

Constraints

2

If a typedef name specifies a variably modified type then it shall have block scope.

Semantics

3

In a declaration whose storage-class specifier is

typedef

, each declarator defines an

identifier to be a typedef name that denotes the type specified for the identifier in the way
described in 6.7.5. Any array size expressions associated with variable length array
declarators are evaluated each time the declaration of the typedef name is reached in the
order of execution. A

typedef

declaration does not introduce a new type, only a

synonym for the type so specified. That is, in the following declarations:

typedef T type_ident;

type_ident D;

type_ident

is defined as a typedef name with the type specified by the declaration

specifiers in

T

(known as ), and the identifier in

D

has the type ‘‘derived-declarator-

type-list T ’’ where the derived-declarator-type-list is specified by the declarators of

D

. A

typedef name shares the same name space as other identifiers declared in ordinary
declarators.

4

EXAMPLE 1

After

typedef int MILES, KLICKSP();

typedef struct { double hi, lo; } range;

the constructions

MILES distance;

extern KLICKSP *metricp;

range x;

range z, *zp;

are all valid declarations. The type of

distance

is

int

, that of

metricp

is ‘‘pointer to function with no

parameter specification returning

int

’’, and that of

x

and

z

is the specified structure;

zp

is a pointer to

such a structure. The object

distance

has a type compatible with any other

int

object.

5

EXAMPLE 2

After the declarations

typedef struct s1 { int x; } t1, *tp1;

typedef struct s2 { int x; } t2, *tp2;

type

t1

and the type pointed to by

tp1

are compatible. Type

t1

is also compatible with type

struct

s1

, but not compatible with the types

struct s2

,

t2

, the type pointed to by

tp2

, or

int

.

§6.7.7 Language

123

6

EXAMPLE 3

The following obscure constructions

typedef signed int t;

typedef int plain;

struct tag {

unsigned t:4;

const t:5;

plain r:5;

};

declare a typedef name

t

with type

signed int

, a typedef name

plain

with type

int

, and a structure

with three bit-field members, one named

t

that contains values in the range [0, 15], an unnamed const-

qualified bit-field which (if it could be accessed) would contain values in either the range [−15, +15] or
[−16, +15], and one named

r

that contains values in one of the ranges [0, 31], [−15, +15], or [−16, +15].

(The choice of range is implementation-defined.) The first two bit-field declarations differ in that

unsigned

is a type specifier (which forces

t

to be the name of a structure member), while

const

is a

type qualifier (which modifies

t

which is still visible as a typedef name). If these declarations are followed

in an inner scope by

t f(t (t));

long t;

then a function

f

is declared with type ‘‘function returning

signed int

with one unnamed parameter

with type pointer to function returning

signed int

with one unnamed parameter with type

signed

int

’’, and an identifier

t

with type

long int

.

7

EXAMPLE 4

On the other hand, typedef names can be used to improve code readability. All three of the

following declarations of the

signal

function specify exactly the same type, the first without making use

of any typedef names.

typedef void fv(int), (*pfv)(int);

void (*signal(int, void (*)(int)))(int);

fv *signal(int, fv *);

pfv signal(int, pfv);

8

EXAMPLE 5

If a typedef name denotes a variable length array type, the length of the array is fixed at the

time the typedef name is defined, not each time it is used:

void copyt(int n)

{

typedef int B[n];

// B

is

n

ints,

n

evaluated now

n += 1;

B a;

// a

is

n

ints,

n

without

+= 1

int b[n];

// a

and

b

are different sizes

for (int i = 1; i < n; i++)

a[i-1] = b[i];

}

124 Language

§6.7.7

 

 

 

 

 

 

 

Content      ..     4      5      6      7     ..