Tải bản đầy đủ (.pdf) (100 trang)

The New C Standard- P14

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (695.45 KB, 100 trang )

6.7.5.3 Function declarators (including prototypes)
1598
spelling of the identifier appearing in the prototype declaration is not significant. Whatever happens there
will not be a quiet change in program behavior. The cost of a syntax violation (the identifier spelling will
need to be changed) has to be balanced against the benefit of including an identifier in the parameter type list.
Some coding guideline documents recommend that any identifiers given for the parameters in a function
prototype declaration have the same spelling as those given in the function definition. Such usage may
provide readers with a reminder of information about what the parameter denotes. A comment could provide
more specific information. Using identifiers in this way also provides visible information that might help
detect changes to a functions interface (e.g., a change in the order of the parameters).
There does not appear to be compelling evidence for any of these options providing sufficient cost/benefit
for a guideline recommendation to be worthwhile.
Example
1 #define abc xyz
2
3 void f_1(int abc);
4
5 int cost_weight_ratio(int cost, int weight);
6
7 int cost_weight_ratio(int weight, int cost)
8 {
9 return cost / weight;
10 }
1598
A declaration of a parameter as “array of type” shall be adjusted to “qualified pointer to type”, where the type
array type
adjust to
pointer to
qualifiers (if any) are those specified within the [ and ] of the array type derivation.
Commentary
This is a requirement on the implementation.


1 void f( int a1[10], /
*
equivalent to int
*
a1
*
/
2 const int a2[10], /
*
equivalent to const int
*
a2
*
/
3 int a3[const 10], /
*
equivalent to int
*
const a3
*
/
4 const int a4[const 10]) /
*
equivalent to const int
*
const a4
*
/
5 { /
*

...
*
/ }
Occurrences of an object, not declared as a parameter, having an array type are implicitly converted to a
pointer type in most contexts. The parameter declaration in:
729 array
converted to
pointer
1 void f(int a[const 10][const 20])
2 { /
*
...
*
/ }
is permitted by the syntax, but violates a constraint.
1568 array pa-
rameter
qualifier only in
outermost
C90
Support for type qualifiers between [ and ], and the consequences of their use, is new in C99.
C
++
This adjustment is performed in C
++
(8.3.5p3) but the standard does not support the appearance of type
qualifiers between [ and ].
Source containing type qualifiers between [ and ] will cause a C
++
translator to generate a diagnostic.

Other Languages
Using qualifiers within the [ and ] of an array declaration may be unique to C.
June 24, 2009 v 1.2
6.7.5.3 Function declarators (including prototypes)
1599
Coding Guidelines
A qualifier appearing outside of
[
and
]
qualifies the element type, not the array type. For non-parameter
declarations this distinction is not significant, the possible consequences are the same. However, the implicit
conversion to pointer type, that occurs for parameters having an array type, means that the distinction is
significant in this case. Experience shows that developers are not always aware of the consequences of this
adjustment to parameters having an array type. The following are two of the consequences of using a qualifier
in the incorrect belief that the array type, rather than the element type, will be qualified:

The
volatile
qualifier— the final effect is very likely to be the intended effect (wanting to
volatile
qualify an object having a pointer type is much rarer than applying such a qualifier to the object it
points at).

The
const
qualifier— attempts to modify the pointed-to objects will cause a translator diagnostic to
be issued and attempts to modify the parameter itself does not require a translator to issue a diagnostic.
Support for qualifiers appearing between
[

and
]
is new in C99 and there is insufficient experience in their
use to know whether any guideline recommendation is cost effective.
1599
If the keyword
static
also appears within the
[
and
]
of the array type derivation, then for each call to the
function
declarator
static
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.
Commentary
Rationale
It would be a significant advantage on some systems for the translator to initiate, at the beginning of the
function, prefetches or loads of the arrays that will be referenced through the parameters.
The use of the keyword
static
within the
[
and
]
of an array type is a guarantee from the developer to the
translator. The translator can assume that the value of the parameter will not be
NULL

(the type of the actual
parameter will have decayed to a pointer to the array element type) and that storage for at least the specified
array
converted
to pointer
729
number of elements will be available.
Rules for forming the composite type of function types, one or more of which included the keyword
static, were given by the response to DR #237.
DR #237
The effect is as if all of the declarations had used static and the largest size value used by any of them. Each
declaration imposes requirements on all calls to the function in the program; the only way to meet all of these
requirements is to always provide pointers to as many objects as the largest such value requires.
1 void WG14_DR_237(int x[static 10]);
2 void WG14_DR_237(int x[static 5]);
3
4 void WG14_DR_237(int x[1])
5 /
*
6
*
Composite type is void WG14_DR_237(int x[static 10])
7
*
/
8 { /
*
...
*
/ }

C90
Support for the keyword static in this context is new in C99.
C
++
Support for the keyword static in this context is new in C99 and is not available in C
++
.
v 1.2 June 24, 2009
6.7.5.3 Function declarators (including prototypes)
1601
Other Languages
Most strongly typed languages require an exact correspondence between the number of elements in the
parameter array declaration and the number of elements in the actual argument passed in a call.
Coding Guidelines
The information provided by constant expressions appearing within the
[
and
]
of the declaration of a
parameter, having an array type, can be of use to static analysis tools. However, in practice because no
semantics was associated with such usage in C90, such arrays were rarely declared. It remains to be seen
how the semantics given to this usage in C99 will change the frequency of occurrence of parameters having
an array type (i.e., will developers use this construct to provide information to translators that might enable
them to generate higher-quality code, or to source code analysis tools to enable them to issue better quality
diagnostics).
Example
Rationale
void fadd( double a[static restrict 10],
const double b[static restrict 10])
{

int i;
for (i = 0; i < 10; i++) {
if (a[i] < 0.0)
return;
a[i] += b[i];
}
return;
}
This function definition specifies that the parameters
a
and
b
are restricted pointers. This is information that an
optimizer can use, for example, to unroll the loop and reorder the loads and stores of the elements referenced
through a and b.
1600
A declaration of a parameter as “function returning type” shall be adjusted to “pointer to function returning
function type
adjust to
pointer to
type”, as in 6.3.2.1.
Commentary
This is a requirement on the implementation. Occurrences of an object, not declared as a parameter, having a
function type are implicitly converted to a pointer type in most contexts.
732 function
designator
converted to type
Other Languages
Languages that support parameters having some form of function type usually have their own special rules
for handling them. A few languages treat function types as first class citizens and they are treated the same as

any other type.
Coding Guidelines
While developers might not be aware of this implicit conversion, their interpretation of the behavior for
uses of the parameter is likely to match what actually occurs (experience suggests that the lack of detailed
knowledge of behavior is not replaced by some misconception that alters developer expectations of behavior).
1601
If the list terminates with an ellipsis
( , ...
), no information about the number or types of the parameters
ellipsis
supplies no
information
after the comma is supplied.
123)
June 24, 2009 v 1.2
6.7.5.3 Function declarators (including prototypes)
1602
Commentary
That is, no information is supplied by the developer to the translator. The ellipsis notation is intended for use
in passing a variable number of argument values (at given positions in the list of arguments) having different
types. The origin of support for variable numbers of arguments was the desire to treat functions handling
input/output in the same as any other function (i.e., the handling of I/O functions did not depend on special
handling by a translator, such as what is needed in Pascal for the read and write functions).
Prior to the publication of the C Standard there existed a programming technique that relied on making use
of information on an implementation’s argument passing conventions (invariably on a stack that either grew
up or down from the storage location occupied by the last named parameter). Recognizing that developers
sometimes need to define functions that were passed variable numbers of arguments the C Committee
introduced the ellipsis notation, in function prototypes. The presence of an ellipsis gives notice to a translator
that different argument types may be passed in calls to that function. Access to any of these arguments is
obtained by encoding information on the expected ordering and type, via calls to library macros, within the

body of the function.
C
++
The C
++
Standard does not make this observation.
Other Languages
The need to pass variable number of arguments, having different types, is a common requirement for
languages that specify functions to handle I/O (some languages, e.g., Fortran, handle I/O as part of the
language syntax). Many languages make special cases for some I/O functions, those that are commonly
required to input or output a number of different values of different types. Having to call a function for each
value and the appropriate function used depending on the type of the value is generally thought onerous by
language designers.
Coding Guidelines
Many coding guideline documents recommend against the use of ellipsis. The view being taken that use of
this notation represents an open-ended charter for uncontrolled argument passing. What are the alternative
and how would developers handle the lack of an ellipsis notation? The following are two possibilities:

Use file scope objects. Any number of file scope objects having any available type could be declared
to be visible to a function definition and the contexts in which it is called.

Use of unions and dummy parameters. In practice, most functions are passed a small number of
optional arguments. A function could be defined to take the maximum number of arguments. In those
cases where a call did not need to pass values to all the arguments available to it, a dummy argument
could be passed. The number of different argument types is also, usually, small. A union type could be
used to represent them.
In both cases the body of the function needs some method of knowing which values to access (as it does
when the ellipsis notation is used).
Is the cure worse than the problem? The ellipsis notation has the advantage of not generating new interface
issues, which the use of file scope objects is likely to do. The advantage to declaring functions to take the

maximum number of arguments (use of union types provides the subset of possible types that argument values
may have) is that information about all possible arguments is known to readers of the function definition. The
benefit of the availability of this information is hard to quantify. However, the cost (developer effort required
to analyze the arguments in the call, working out which ones are dummy and which unions members are
assigned to) is likely to be significant.
Recommending that developers not use the ellipsis notation may solve one perceived problem, but because
of the cost of the alternatives does not appear to result in any overall benefit.
While there is existing code that does not use the macros in the
<stdarg.h>
header to access arguments,
but makes use of information on stack layout to access arguments, such usage is rarely seen in newly written
code. A guideline recommendation dealing with this issue is not considered worthwhile.
v 1.2 June 24, 2009
6.7.5.3 Function declarators (including prototypes)
1603
1602
The special case of an unnamed parameter of type
void
as the only item in the list specifies that the function
parameter
type void
has no parameters.
Commentary
The base document had no special syntax for specifying a function declaration that took no parameters.
The convention used to specify this case was an empty parameter list. However, an empty parameter list
was also used to indicate another convention (which is codified in the standard), a function declaration that
1608 function
declarator
empty list
provided no information about the arguments it might take (although it might take one or more). Use of the

keyword
void
provides a means of explicitly calling out the case of a function taking no parameters (had
C90 specified that an empty parameter list denoted a function taking no parameters, almost every existing C
program would have become non standards conforming).
C90
The C90 Standard was reworded to clarify the intent by the response to DR #157.
Other Languages
Strongly typed languages treat a function declared with no parameters as a function that does not take any
arguments, and they sometimes (e.g., Pascal) require the empty parentheses to be omitted from the function
call. Other languages vary in their handling of this case.
Example
1 typedef void Void;
2
3 extern int f(Void);
4
5 int f(Void)
6 { /
*
...
*
/ }
1603
parameter
declaration
typedef name
in parentheses
If, iIn a parameter declaration, a single typedef name in parentheses is taken to be an abstract declarator
that specifies a function with a single parameter, not as redundant parentheses around the identifier for a
declarator. an identifier can be treated as a typedef name or as a parameter name, it shall be taken as a

typedef name.
Commentary
This is a requirement on the implementation. It is an edge case of the language definition. In:
1 typedef int T;
2 int f(int
*
(T));
f
is declared as function returning
int
, taking a single parameter of type pointer to function returning
int
and taking a parameter of type
T
. Without the above rule it could also be interpreted as function returning
int, taking a single parameter of type pointer to int, with redundant parentheses around the identifier T.
Wording changes to C90, made by the response to DR #009, were not made to the text of C99 (because
the committee thought that forbidding implicit types would eliminate this problem, but an edge case still
remained). The response to DR #249 agreed that these changes should have been made and have now been
made.
In the following declaration of
WG14_N852
the identifier
what
is treated as a typedef name and violates
the constraint that a function not return a function type.
1592 function
declarator return
type
1 typedef int what;

2
3 int WG14_N852(int (what)(int)); /
*
Constraint violation.
*
/
The case of empty parentheses is discussed elsewhere.
1628 type name
empty parentheses
June 24, 2009 v 1.2
6.7.5.3 Function declarators (including prototypes)
1604
C90
The response to DR #009 proposed adding the requirement: “If, in a parameter declaration, an identifier can
be treated as a typedef name or as a parameter name, it shall be taken as a typedef name.”
Other Languages
Most languages do not allow the identifier being declared to be surrounded by parentheses. Neither do they
have the C style of declarators.
Coding Guidelines
Developers are unlikely to be familiar with this edge case of the language. However, the types involved
(actual and incorrectly deduced) will be sufficiently different that a diagnostic is very likely to be produced
for uses of an argument or parameter based on the incorrect type.
1604
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.
Commentary
Functions declared with a parameter having an incomplete structure or union type will only be callable after
the type is completed. For instance:

1 void f_1(struct T); /
*
No prior declaration of T visible here, a new type that can never be completed.
*
/
2 struct T;
3 void f_2(struct T); /
*
Prior declaration of T visible here, refers to existing type.
*
/
4 /
*
5
*
Cannot define an argument to have type struct T at
6
*
this point so the function is not yet callable.
7
*
/
8 struct T {
9 int mem;
10 };
11 /
*
12
*
Can now define an object to pass as an argument to f_2.

13
*
/
The following declarations of
f
are all compatible with each other (the operand of
sizeof
does not need to
be evaluated in this context):
1 int f(int n, int a[
*
]);
2 int f(int n, int a[sizeof(int [
*
][
*
])]);
3 int f(int n, int a[sizeof(int [n][n+1])]);
Use of incomplete types where the size is not needed is discussed elsewhere.
footnote
109
1465
C90
Support for the [
*
] notation is new in C99.
C
++
The wording:
8.3.5p6

If the type of a parameter includes a type of the form “pointer to array of unknown bound of
T
” or “reference to
array of unknown bound of T,” the program is ill-formed.
87)
does not contain an exception for the case of a function declaration.
Support for the [
*
] notation is new in C99 and is not specified in the C
++
Standard.
Other Languages
Most languages require that structure types appearing in function declarations be complete. A number of
languages provide some form of [
*
] notation to indicate parameters having a variable length array type.
v 1.2 June 24, 2009
6.7.5.3 Function declarators (including prototypes)
1608
Coding Guidelines
Functions declared with a parameter having an incomplete structure or union type might be regarded as
redundant declarations. This issue is discussed elsewhere.
190 redundant
code
Example
1 void f_1(struct T_1
*
p_1);
2
3 struct T_2;

4 void f_2(struct T_2
*
p1);
5
6 void f_3(void)
7 {
8 struct T_1
*
loc_1 = 0;
9 struct T_2
*
loc_2 = 0;
10
11 f_1(loc_1); /
*
Constraint violation, different structure type.
*
/
12 f_2(loc_2); /
*
Argument has same structure type as parameter.
*
/
13 }
1605
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.
Commentary
The only storage-class specifier that can occur on a parameter declaration is
register

. The interpretation of
1593 parameter
storage-class
objects having this storage-class only applies to accesses to them, which can only occur in the body of the
function definition.
Coding Guidelines
Whether or not function declarations are token for token identical to their definitions is not considered
worthwhile addressing in a guideline recommendation.
1606
An identifier list declares only the identifiers of the parameters of the function.
Commentary
In a function declaration such a list provides no information to the translator, but it may provide useful
commentary for readers of the source (prior to the availability of function prototypes). In a function definition
this identifier list provides information to a translator on the number and names of the parameters.
C
++
This form of function declarator is not available in C
++
.
1607
An empty list in a function declarator that is part of a definition of that function specifies that the function has
no parameters.
Commentary
For a definition of a function, for instance
f
, there is no difference between the forms
f()
and
f(void)
.

There is a difference for declarations, which is covered in the following C sentence.
Other Languages
An empty list is the notation commonly used in other languages to specify that a function has no parameters.
Some languages also require that the parentheses be omitted.
Coding Guidelines
Differences in the costs and benefits of using either an empty list or requiring the use of the keyword
void
are not sufficient to warrant a guideline recommendation dealing with this issue.
June 24, 2009 v 1.2
6.7.5.3 Function declarators (including prototypes)
1611
1608
The empty list in a function declarator that is not part of a definition of that function specifies that no information
function
declarator
empty list
about the number or types of the parameters is supplied.
124)
Commentary
This specification differs from that of a function declarator that whose parameter list contains the keyword
void.
parameter
type void
1602
C
++
The following applies to both declarations and definitions of functions:
8.3.5p2
If the parameter-declaration-clause is empty, the function takes no arguments.
A call made within the scope of a function declaration that specifies an empty parameter list, that contains

arguments will cause a C
++
translator to issue a diagnostic.
Common Implementations
Some translators remember the types of the arguments used in calls to functions declared using an empty list.
Inconsistencies between the argument types in different calls being flagged as possibly coding defects.
Coding Guidelines
The guideline recommendation specifying the use of function prototypes is discussed elsewhere.
function
declaration
use prototype
1810.1
1609
123) The macros defined in the
<stdarg.h>
header (7.15) may be used to access arguments that correspond
footnote
123
to the ellipsis.
Commentary
These are the va_
*
macros specified in the library section.
1610
124) See “future language directions” (6.11.6).footnote
124
1611
For two function types to be compatible, both shall specify compatible return types.
125)
function

compatible types
Commentary
This is a necessary conditions for two function declarations to be compatible. The second condition is
compati-
ble type
if
631
specified in the following C sentence.
C
++
The C
++
Standard does not define the concept of compatible type, it requires types to be the same.
compati-
ble type
if
631
3.5p10
After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified
by all declarations referring to a given object or function shall be identical, . . .
8.3.5p3
All declarations for a function with a given parameter list shall agree exactly both in the type of the value
returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of
the function type.
If one return type is an enumerated type and the another return type is the compatible integer type. C would
consider the functions compatible. C
++
would not consider the types as agreeing exactly.
Other Languages
Compatibility of function types only becomes an issue when a language’s separate translation model allows

more than one declaration of a function, or when pointers to functions are supported. In these cases the
requirements specified are usually along the lines of those used by C.
v 1.2 June 24, 2009
6.7.5.3 Function declarators (including prototypes)
1614
Coding Guidelines
The rationale for the guideline recommendations on having a single textual declaration and including the
422.1 identifier
declared in one file
header containing it in the source file that defines the function is to enable translators to check that the two
1818.1 identifier
definition
shall #include
declarations are compatible.
1612
Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of
the ellipsis terminator;
Commentary
This condition applies if both function declarations use prototypes.
C
++
A parameter type list is always present in C
++
, although it may be empty.
Other Languages
Most other languages require that the parameter type lists agree in the number of parameters.
Coding Guidelines
If the guideline recommendation specifying the use of prototypes is followed the parameter type lists will
1810.1 function
declaration

use prototype
always be present.
1613
corresponding parameters shall have compatible types.
Commentary
This requirement applies between two function declarations, not between the declaration of a function and a
call to it.
998 function call
arguments agree
with parameters
C
++
8.3.5p3
All declarations for a function with a given parameter list shall agree exactly both in the type of the value
returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of
the function type.
The C
++
Standard does not define the concept of compatible type, it requires types to be the same. If one
631 compati-
ble type
if
parameter type is an enumerated type and the corresponding parameter type is the corresponding compatible
integer type. C would consider the functions to be compatible, but C
++
would not consider the types as being
the same.
Other Languages
Most languages require the parameter types to be compatible.
1614

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.
Commentary
This C sentence deals with the case of a function prototype (which may or may not be a definition) and an
old style function declaration that is not a definition. One possible situations where it can occur is where
a function definition has been rewritten using a prototype, but there are still calls made to it from source
where an old style declaration is visible. Function prototypes were introduced in C90 (based on the C
++
specification). The committee wanted to ensure that developers could gradually introduce prototypes in to
existing code. For instance, using prototypes for newly written functions. It was therefore necessary to deal
June 24, 2009 v 1.2
6.7.5.3 Function declarators (including prototypes)
1615
with the case of source code containing so called old style and function prototype declarations for the same
function.
Calls where the visible function declaration uses an old style declaration, have their arguments promoted
using the default argument promotions. The types of the promoted arguments are required to be compatible
default ar-
gument
promotions
1009
with the parameter types in the function definition (which uses a prototype). This requirement on the
parameter types in the function definition ensures that argument/parameter storage layout calculations (made
by an implementation) are consistent.
The ellipsis terminator is a special case. Some translators are known to handle arguments passed to this
parameter differently than when there is a declared type (the unknown nature of the arguments sometimes
means that this special processing is a necessity). Given the possibility of this implementation technique the
Committee decided not to require the behavior to be defined if the two kinds of declarations were used.

There can be no check on the number of parameters in the two declarations, since one of them does not
have any. This issue is covered elsewhere.
arguments
same number
as parameters
1010
C
++
The C
++
Standard does not support the C identifier list form of parameters. An empty parameter list is
interpreted differently:
8.3.5p2
If the parameter-declaration-clause is empty, the function takes no arguments.
The two function declarations do not then agree:
3.5p10
After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified
by all declarations referring to a given object or function shall be identical, . . .
A C
++
translator is likely to issue a diagnostic if two declarations of the same function do not agree (the object
code file is likely to contain function signatures, which are based on the number and type of the parameters
in the declarations).
Other Languages
Very few languages (Perl does) have to deal with the issue of developers being able to declare functions using
two different sets of syntax rules.
Common Implementations
Implementations are not required to, and very few do, issue diagnostics if these requirements are not
met. Whether programs fail to work as expected, if these requirements are not met, often depends on the
characteristics of the processor. For those processors that have strict alignment requirements translators

alignment 39
usually assign parameters at least the same alignment as those of the type
int
(ensuring that integer types
with less rank are aligned on the storage boundaries of their promoted type). For processors that have more
relaxed alignment requirements, or where optimisations are possible for the smaller integer types, parameters
having a type whose rank less than that of the type
int
are sometime assigned a different storage location
than if they had a type of greater rank. In this case the arguments, which will have been treated as having at
least type int, will be at different storage locations in the function definitions stack frame.
Coding Guidelines
This case shows that gradually introducing function prototypes into existing source code can cause behavioral
differences that did not previously exist. Most of the benefits of function prototype usage come from the
checks that translators perform at the point of call. Defining a function using prototype notation and having
an old style function declaration visible in a header offers little benefit (unless the function has internal
linkage and is visible at all the places it is called). If an existing old style function definition is modified
to use a function prototype in its definition, then it is effectively new code and any applicable guideline
recommendations apply.
v 1.2 June 24, 2009
6.7.5.3 Function declarators (including prototypes)
1615
1615
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.
Commentary
In this C sentence the two types are a function declaration that uses a prototype and a function definition that
uses an old style declaration. In both cases the developer has specified the number and type of two sets of

parameters. Both declarations are required to agree in the number of parameters (if the number and type of
each parameter agrees there cannot be an ellipsis terminator in the function prototype).
A function defined using an identifier list will be translated on the basis that the arguments, in calls to
it, have been promoted according to the default argument promotions. Calls to functions where the visible
1009 default ar-
gument
promotions
declaration is a function prototype will be translated on the basis that the definition expects the arguments
to be converted at the point of call (to the type of the corresponding parameter). This C sentence describes
those cases where the two different ways of handling arguments results in the same behavior. In all other
cases the behavior is undefined.
C
++
The C
++
Standard does not support the C identifier list form of parameters.
8.3.5p2
If the parameter-declaration-clause is empty, the function takes no arguments.
The C
++
Standard requires that a function declaration always be visible at the point of call (5.2.2p2). Issues
involving argument promotion do not occur (at least for constructs supported in C).
1 void f(int, char);
2 void f(char, int);
3 char a, b;
4 f(a,b); // illegal: Which function is called? Both fit
5 // equally well (equally badly).
Common Implementations
Implementations are not required to, and very few do, issue diagnostics if these requirements are not met.
Coding Guidelines

Adding prototype declarations to an existing program may help to detect calls made using arguments that are
not compatible with the corresponding functions parameters, but they can also change the behavior of correct
calls if they are not properly declared. Adhering to the guideline recommendation specifying that the header
containing the function prototype declaration be
#include
d in the source file that defines the function is not
1818.1 identifier
definition
shall #include
guaranteed to cause a translator to issue a diagnostic if the above C sentence requirements are not met. The
following guideline recommendation addresses this case.
Cg
1615.1
If a program contains a function that is declared using both a prototype and an old style declaration,
then the type of each parameter in the prototype shall be compatible with the type of corresponding
parameter in the old style declaration after the application of the default argument promotions to those
parameter types.
Example
file_1.c
1 int f(p_1)
2 signed char p_1; /
*
Expecting argument to have been promoted to int.
*
/
3 {
4 return p_1+1;
5 }
June 24, 2009 v 1.2
6.7.5.3 Function declarators (including prototypes)

1616
file_2.c
1 int f(signed char p_1);
2
3 int g(void)
4 {
5 return f(’0’); /
*
Argument converted to type signed char.
*
/
6 }
1616
(In the determination of type compatibility and of a composite type, each parameter declared with function or
parameter
qualifier in com-
posite type
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.)
Commentary
This specifies the relative ordering of requirements on adjusting types, creating composite types and de-
parame-
ter type
adjusted
1835
function
composite type
646
termining type compatibility. While the composite type of a parameter is always its unqualified type, the
compati-

ble type
if
631
compos-
ite type
642
wording of the response to DR #040 question 1 explains how composite types are to be treated.
DR #040 question 1
The type of a parameter is independent of the composite type of the function, . . .
In the body of a function the type of a parameter is the type that appears in the function definition, not any
composite type. In the following example
DR_040_a
and
DR_040_b
have the same composite types, but the
parameter types are not the same in the bodies of their respective function definitions.
1 void DR_040_a(const int c_p);
2 void DR_040_a( int p)
3 {
4 p=1; /
*
Not a constraint violation.
*
/
5 }
6
7 void DR_040_b( int p);
8 void DR_040_b(const int c_p)
9 {
10 c_p=1; /

*
A constraint violation.
*
/
11 }
Calls to functions will make use of information contained in the composite type. The fact that any parameter
prior decla-
ration visible
649
types, in the composite type, will be unqualified is not significant because it is the unqualified parameter type
that is used when processing the corresponding arguments.
argument
type may be
assigned
999
C90
The C90 wording:
(For each parameter declared with function or array type, its type for these comparisons is the one that results
from conversion to a pointer type, as in 6.7.1. For each parameter declared with qualified type, its type for these
comparisons is the unqualified version of its declared type.)
was changed by the response to DR #013 question 1 (also see DR #017q15 and DR #040q1).
C
++
The C
++
Standard does not define the term composite type. Neither does it define the concept of compatible
compos-
ite type
642
type, it requires types to be the same.

compati-
ble type
if
631
The C
++
Standard transforms the parameters’ types and then:
8.3.5p3
If a
storage-class-specifier
modifies a parameter type, the specifier is deleted. [Example:
register
char
*
becomes
char
*
—end example] Such
storage-class-specifiers
affect only the definition of the
parameter within the body of the function; they do not affect the function type. The resulting list of transformed
parameter types is the function’s parameter type list.
v 1.2 June 24, 2009
6.7.5.3 Function declarators (including prototypes)
1619
It is this parameter type list that is used to check whether two declarations are the same.
Coding Guidelines
Adhering to the guideline recommendation specifying the use of function prototypes does not guarantee that
1810.1 function
declaration

use prototype
the composite type will always contain the same parameter type information as in the original declarations.
It is possible that while reading the source of a function definition a developer will make use of information,
that exists in their memory, that is based on the functions declaration in a header, rather than the declaration at
the start of the definition. The consequences of this usage, in those cases where the parameter types differ in
qualification, do not appear to be sufficiently costly (in unintended behavior occurring) to warrant a guideline
recommendation.
Example
1 void f_1(int p_a[3]);
2 void f_1(int
*
p_a ); /
*
Compatible with previous f_1
*
/
1617
EXAMPLE 1 The declaration EXAMPLE
function return-
ing pointer to
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.
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.
Commentary
The algorithm for reading declarations involving both function and pointer types follows a right then left rule
similar to that used for reading declarations involving array and pointer types. However, it is not possible to
1587 EXAMPLE
array of pointers
declare a function of functions, so only one function type on the right is consumed.
C
++
Function declared with an empty parameter type list are considered to take no arguments in C
++
.
1618
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.
Commentary
The parentheses are necessary because
int
*
apfi[3](int
*
x, int
*
y);
declares
apfi
to be an array of
three functions returning pointer to int (which is a constraint violation).
1567 array element
not function type
June 24, 2009 v 1.2
6.7.5.3 Function declarators (including prototypes)
1621
1619
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.
Commentary
The declaration
1 int (
*
(
*
fpfpfi(int (

*
)(long), int))(int, ...))(void);
declares a function
fpfpfi
that returns a pointer to a function returning a pointer to a function returning an
int
involves parenthesizing part of the existing declaration and adding information on the parameters (in this
case (void)).
1620
125) If both function types are “old style”, parameter types are not compared.footnote
125
Commentary
For old style function types there is no parameter information to compare (technically there is information
available in one case, when one is a definition; however, the standard considers this information to be local to
the function body).
C
++
The C
++
Standard does not support old style function types.
Other Languages
Some languages do not specify whether declarations of the same function should be compared for compati-
bility.
1621
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;
}
C90
Support for variably modified types is new in C99.
C
++
Support for variably modified types is new in C99 and is not specified in the C
++
Standard.
v 1.2 June 24, 2009
6.7.6 Type names
1624
Coding Guidelines

The expression
n
*
m+300
occurs in a number of places in the source. Replacing this expression with a
symbolic name will reduce the probability of future changes to one use of this expression not being reflected
in other uses.
1622
EXAMPLE 5 The following are all compatible function prototype declarators. EXAMPLE
compatible func-
tion prototypes
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.)
Commentary
The conversion of parameters having array type to pointer type allows the
restrict
type qualifier to occur
729 array
converted to
pointer
in this context.
1623
Forward references: function definitions (6.9.1), type names (6.7.6).
6.7.6 Type names
1624
ab-
stract declarator
syntax
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
[ assignment-expression
opt
]
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
)
Commentary

An abstract declarator specifies a type without defining an associated identifier. The term type-name is
slightly misleading since there is no name, the type is anonymous.
The wording was changed by the response to DR #289 and makes the syntax consistent with that for
direct-declarator.
1547 declarator
syntax
C90
Support for the form:
direct-abstract-declarator
opt
[
*
]
is new in C99. In the form:
June 24, 2009 v 1.2
6.7.6 Type names
1627
direct-abstract-declarator
opt
[ assignment-expression
opt
]
C90 only permitted constant-expression
opt
to appear between [ and ].
C
++
The C
++
Standard supports the C90 forms. It also includes the additional form (8.1p1):

direct-abstract-declarator
opt
( parameter-declaration-clause )
cv-qualifier-seq
opt
exception-specification
opt
Other Languages
A few languages (e.g., Algol 68) have a concept similar to that of abstract declarator (i.e., an unnamed type
that can appear in certain contexts, such as casts).
Coding Guidelines
The term type is applied generically to all type declarations, whether they declare identifiers or not. Developers
do not appear to make a distinction between declarators and abstract declarators.
More cognitive effort is needed to comprehend an abstract declarator than a declarator because of the
additional task of locating the position in the token sequence where the identifier would have been, had it not
been omitted. Whether there is sufficient benefit in providing an identifier (taking into account the costs of
providing it in the first place) to make a guideline recommendation worthwhile is a complex question that
your author does not yet feel capable of answering.
Semantics
1625
In several contexts, it is necessary to specify a type.
Commentary
These contexts are: a compound literal, the type in a cast operation, the operand of
sizeof
, and parameter
types in a function prototype declaration that omits the identifiers.
1626
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.
126)

Commentary
This defines the term type name (and saying in words what is specified in the syntax).
C
++
Restrictions on the use of type names in C
++
are discussed elsewhere.
sizeof
constraints
1118
cast
scalar or void type
1134
function
declarator
return type
1592
Coding Guidelines
A type-name is syntactically a declaration and it is possible to declare identifiers using it. For instance:
1 void f_1(int p)
2 {
3 p=(enum {apple, orange, pair})sizeof(enum {brazil, cashu, almond});
4 }
5
6 struct T {int mem;} f_2(void)
7 {
8 struct T loc;
9 /
*
...

*
/
10 return loc;
11 }
Such uses are not common and might come as a surprise to some developers. The cost incurred by this usage
is that readers of the source may have to spend additional time searching for the identifiers declared, because
they do not appear in an expected location. There is no obvious worthwhile benefit (although there is a C
++
compatibility benefit) in a guideline recommendation against this usage.
v 1.2 June 24, 2009
6.7.6 Type names
1628
1627
EXAMPLE The constructions EXAMPLE
abstract
declarators
(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.
Commentary
The following is one algorithm for locating where the omitted identifier occurs in an abstract declarator.

Starting on the left and working right:
1.
skip all keywords, identifiers, and any matched pairs of braces along with their contents (the latter are
struct/union/enum declarations), then
2.
skip all open parentheses, asterisks, and type qualifiers. The first unskipped token provides the context
that enables the location of the omitted identifier to be deduced:

A
[
token is the start of an array specification that appears immediately after the omitted identifier.

A
type-specifier
is the start of the
declaration-specifier
of the first parameter of a
parameter list. The omitted identifier occurs immediately before the last skipped open parenthesis.

A
)
token immediately after a
(
token is an empty parameter list. The omitted identifier occurs
immediately before the last skipped open parenthesis.

A
)
token that is not immediately after a
(

token is the end of a parenthesized abstract declarator
with no array or function specification. The omitted identifier occurs immediately before this
)
token.
C90
Support for variably length arrays is new in C99.
C
++
Support for variably length arrays is new in C99 and is not specified in the C
++
Standard.
1628
126) As indicated by the syntax, empty parentheses in a type name are interpreted as “function with no
footnote
126
type name
empty paren-
theses
parameter specification”, rather than redundant parentheses around the omitted identifier.
Commentary
That is in the following declaration of f:
1 typedef char x;
2 void f(int (x), /
*
Function returning int having a single char parameter.
*
/
3 int ()); /
*
Function returning int with no parameter information specified.

*
/
4
5 void g(int (y) /
*
Parameter having type int and name y.
*
/
6 );
The behavior of parentheses around an identifier is also described elsewhere.
1603 parameter
declaration
typedef name in
parentheses
June 24, 2009 v 1.2
6.7.7 Type definitions
1629
Other Languages
This notation is used by many languages (many of which don’t allow parentheses around identifiers).
Coding Guidelines
The guideline recommendation dealing with the use of prototypes in function declarators is applicable here.
function
declaration
use prototype
1810.1
6.7.7 Type definitions
1629
typedef name
syntax
typedef-name:

identifier
Commentary
A typedef name exists in the same name space as ordinary identifiers. The information that differentiates an
name space
ordinary identifiers
444
identifier as a
typedef-name
, from other kinds of identifiers is the visibility, or not, of a typedef definition
of that identifier. For instance, given the declarations:
1 typedef int type_ident;
2 type_ident(D_1); /
*
Function call or declaration of D_1?
*
/
3 type_ident
*
D_2; /
*
Multiplication or declaration of D_2?
*
/
it is not possible to decide using syntax only (i.e., without the use of semantic information from a symbol
table) whether type_ident(D_1); is a function call or a declaration of D_1 using redundant parentheses.
There are some contexts where the status of an identifier as a
typedef-name
can be deduced. For instance,
the token sequence
; x y;

is either a declaration of
y
to have the type denoted by
x
, or it is a violation of
syntax (because a definition of x as a typedef name is not visible).
Other Languages
Languages invariably use ordinary identifiers to indicate both objects and their equivalent (if supported) of
typedef names.
Common Implementations
The syntax of most languages is such that it is possible to parse their source without the need for a symbol
table holding information on prior declarations. It is also usually possible to parse them by looking ahead a
single token in the input stream (tools such as bison support such language grammars).
The syntax of C declarations and the status of
typedef-name
as an identifier token creates a number
of implementation difficulties. The parser either needs access to a symbol table (so that it knows which
identifiers are defined as
typedef-name
s), or it needs to look ahead more than one token and be able to
handle more than one parse of some token sequences. Most implementations use the symbol table approach
(which in practice is more complicated than simply accessing a symbol table; it is also necessary to set or
reset a flag based on the current syntactic context, because an identifier should only be looked up, to find out
if it is currently defined as a typedef-name in a subset of the contexts in which an identifier can occur).
Coding Guidelines
It is common developer practice to use the term type name (as well as the term typedef name) to refer to the
identifier defined by a typedef declaration. There is no obvious benefit in attempting to change this common
developer usage. The issue of naming conventions for typedef names is discussed elsewhere.
typedef
naming con-

ventions
792
The higher-level source code design issues associated with the use of typedef names are discussed below.
Given some of the source reading techniques used by developers it is possible that a typedef name appearing
reading
kinds of
770
in a declaration will be treated as one of the identifiers being declared. This issue is discussed elsewhere.
declaration
syntax
1348
This coding guideline subsection discusses the lower-level issues, such as processing of the visible source by
readers and naming conventions.
The only time the visible form of declarations is likely to contain two identifiers adjacent to each other
(separated only by white space) is when a typedef name is used (such adjacency can also occur through
v 1.2 June 24, 2009
6.7.7 Type definitions
1630
the use of macro names, but is rare). The two common cases are for the visible source lines containing
declarations to either start with a keyword (e.g.,
int
or
struct
) or an identifier that is a member of an
identifier list (e.g., a list of identifiers being declared).
Some of the issues involved in deciding whether to use a typedef name of a tag name, for structure and
union types, is discussed elsewhere. Using a typedef name provides a number of possible benefits, including
792 tag
naming con-
ventions

the following:

Being able to change the type used in more than declaration by making a change to one declaration (the
typedef name). In practice this cost saving is usually only a significant factor when the type category
553 type category
is not changed (e.g., an integer type is changed to an integer type, or a structure type is changed to
another structure type). In this case the use of objects declared using these typedef name as operands
in expressions does not need to be modified (changing, for instance, an array type to a structure type is
likely to require changes to the use of the object in expressions).

The spelling of the type name may providing readers with semantic information about the type that
would not be available if the sequence of tokens denoting the type had appeared in the source. The
issue of providing semantic information via identifier spellings is discussed elsewhere.
792 identifier
semantic associa-
tions

The use of typedef names is sometimes recommended in coding guideline documents because it offers
a mechanism for hiding type information. However, readers are likely to be able to deduce this (from
looking at uses of an object), and are also likely to need to know an objects type category (which is
553 type category
probably the only significant information that type abstraction is intended to hide).
Usage
A study by Neamtiu, Foster, and Hicks
[1015]
of the release history of a number of large C programs, over 3-4
years (and a total of 43 updated releases), found that in 16% of releases one or more existing typedef names
had the type they defined changed.
[1014]
Table 1629.1:

Occurrences of types defined in a
typedef
definition (as a percentage of all types appearing in
typedef
definitions).
Based on the translated form of this book’s benchmark programs.
Type Occurrences Type Occurrences
struct 58.00 unsigned long 1.47
enum 9.50 int *() 1.46
other-types 8.86 enum *() 1.46
struct
*
6.97 union 1.38
unsigned int 2.68 long 1.29
int 2.46 void *() 1.18
unsigned char 2.21 unsigned short 1.07
Constraints
1630
If a typedef name specifies a variably modified type then it shall have block scope.
Commentary
Allowing a typedef name to occur at file scope appears to be useful; the identifier name provides a method of
denoting the same type in declarations in different functions, or translation units. However, the expression
denoting the number of elements in the array has to be evaluated during program execution. The committee
decided that this evaluation would occur when the type declaration was encountered during program execution.
1632 array size
evaluated when
declaration
reached
This decision effectively prevents any interpretation being given for such declarations at file scope.
C90

Support for variably modified types is new in C99.
June 24, 2009 v 1.2
6.7.7 Type definitions
1632
C
++
Support for variably modified types is new in C99 and not specified in the C
++
Standard.
Coding Guidelines
The extent to which more than one instance of a variably modified type using the same size expression and
element type will need to be defined in different functions is not known. A macro definition provides one
mechanism for ensuring the types are the same. Variably modified types are new in C99 and experience with
their use is needed before any guideline recommendations can be considered.
Semantics
1631
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.
Commentary
The association between
typedef
and storage class is a syntactic one (they share the same declarator forms),
not a semantic one.
Other Languages
Many languages that support developer defined type names define a syntax that is specific to that usage
(which may simply involve the use of a different keyword, for instance Pascal requires that type definitions
be introduced using the keyword type).
Coding Guidelines

The issues involved in declarations that declare more than one identifier are discussed elsewhere.
declaration
visual layout
1348
Example
1 extern int i, j[3];
2 typedef int I, J[3];
1632
Any array size expressions associated with variable length array declarators are evaluated each time the
array size
evaluated when
declaration
reached
declaration of the typedef name is reached in the order of execution.
Commentary
Rationale
Using a
typedef
to declare a variable length array object (see §6.7.5.2) could have two possible meanings.
Either the size could be eagerly computed when the
typedef
is declared, or the size could be lazily computed
when the object is declared. For example
{
typedef int VLA[n];
n++;
VLA object;
// ...
}
The question arises whether

n
should be evaluated at the time the type definition itself is encountered or each
time the type definition is used for some object declaration. The Committee decided that if the evaluation
were to take place each time the typedef name is used, then a single type definition could yield variable
length array types involving many different dimension sizes. This possibility seemed to violate the spirit of type
definitions. The decision was made to force evaluation of the expression at the time the type definition itself is
encountered.
v 1.2 June 24, 2009
6.7.7 Type definitions
1633
In other words, a typedef declaration does not act like a macro definition (i.e., with the expression evaluated
for every invocation), but like an initialization (i.e., the expression is evaluated once).
C90
Support for variable length array declarators is new in C99.
C
++
Support for variable length array declarators is new in C99 and is not specified in the C
++
Standard.
Other Languages
Other languages either follow the C behavior (e.g., Algol 68), or evaluated on each instance of a typedef
name usage (e.g., Ada).
Coding Guidelines
While support for this construct is new in C99 and at the time of this writing insufficient experience with
its use is available to know whether any guideline recommendation is worthwhile, variable length array
declarations can generate side effects, a known problem area. The guideline recommendation applicable to
side effects in declarations is discussed elsewhere.
187.2 full declarator
all orderings give
same type

Example
In the following the objects q and r will contain the same number of elements as the object p.
1 void f(int n)
2 {
3 START_AGAIN: ;
4
5 typedef int A[n];
6
7 A p;
8 n++;
9 A q;
10
11 {
12 int n = 99;
13 A r; /
*
Uses the object n visible when the typedef was defined.
*
/
14 }
15
16 if ((n % 4) != 0)
17 goto START_AGAIN;
18
19 if ((n % 5) != 0)
20 f(n+2)
21 }
1633
A typedef declaration does not introduce a new type, only a synonym for the type so specified. typedef
is synonym

Commentary
Typedef names provide a mechanism for easily modifying (by changing a single definition) the types of a
set of objects (those declared using a given typedef name) declared within the source code. The fact that a
typedef
only introduces a synonym for a type, not a new type, is one of the reasons C is considered to be a
typed language but not a strongly typed language.
It is not possible to introduce a new name for a tag. For instance:
1 typedef oldtype newtype; /
*
Supported usage.
*
/
2 typedef struct oldtype struct newtype; /
*
Syntax violation.
*
/
One difference between a typedef name and a tag is that the former may include type qualifier information.
For instance, in:
June 24, 2009 v 1.2
6.7.7 Type definitions
1634
1 typedef const struct T {
2 int mem;
3 } cT;
the typedef name cT is const qualified, while the tag T is not.
Other Languages
Some languages regard all typedef declarations as introducing new types. A few languages have different
kinds of typedef declarations, one introducing a new type and the other introducing a synonym.
Common Implementations

Some static analysis tools support an option that allows typedef names to be treated as new (i.e., different)
types (which can result in diagnostics being issued when mixing operands having types).
Coding Guidelines
Experienced users of strongly typed languages are aware of the advantages of having a typedef that creates a
new type (rather than a synonym). They enable the translator to aid the developer (by detecting mismatches
and issuing diagnostics) in ensuring that objects are not referenced in inappropriate contexts. Your author is
not aware of any published studies that have investigated the costs and benefits of strong typing (there have
been such studies comparing so called strongly typed languages against C, but it is not possible to separate
out the effects of typing from other language features). Also there does not appear to have been any work
that has attempted to introduce strong typing into C in a commercial environment.
Your author’s experience (based on teaching Pascal and analyzing source code written in it) is that it
takes time and practice for developers to learn how to use strong type names effectively. While the concepts
behind individual type names may be quickly learned and applied, handling the design decisions behind the
interaction between different type names requires a lot of experience. Even in languages such as Pascal and
Ada, where implementations enforced strong typing, developers still required several years of experience to
attain some degree of proficiency.
Given C’s permissive type checking (e.g., translators are not required to perform much type checking on
the standard integer types), only a subset of the possible type differences are required to cause a diagnostic
standard
integer types
493
to be generated. Given the lack of translator support for strong type checking and the amount of practice
needed to become proficient in its use, there is no cost/benefit in recommending the use of typedef names for
type checking purposes. The cost/benefit of using typedef names for other purposes, such as enabling the
types of a set of objects to be changed by a single modification to the source, may be worthwhile.
Measurements of the translated form of this book’s benchmark programs show that typedef names occur
much more frequently than the tag names (by a factor of 1.7:1; although this ratio is switched in some
programs, e.g., tag names outnumber typedef names by 1.5:1 in the Linux kernel). Why is this (and should
the use of typedef names be recommended)? The following are some of the possible reasons:
• Developers new to C imitate what they see others doing and what they read in books.


The presence of the
struct
/
union
/
enum
keyword is considered to be useful information, highlighting
the kind of type that is being used (structure types in particular seem to be regarded as very different
animals than other types). While this rationale goes against the design principle of hiding representation
details, experience shows that uses of structure types are rarely changed to non-structure types.

The usage is driven by the least effort principle being applied by a developer at the point where the
structure type is defined. While the effort needed in subsequent references to the type may be less,
had a typedef name had been used, the cost of subsequent uses is not included in the type definition
decision process.
v 1.2 June 24, 2009
6.7.7 Type definitions
1638
1634
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 T ),
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.
Commentary
A declaration of the form
type_ident D;
is the only situation where two identifier tokens are adjacent in
the preprocessed source.
C
++
This example and its associated definition of terms is not given in the C
++
Standard.
1635
A typedef name shares the same name space as other identifiers declared in ordinary declarators.
Commentary
This issue is discussed elsewhere.
444 name space
ordinary identifiers
1636
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.
Coding Guidelines
The use of uppercase in typedef names is discussed elsewhere.
792 typedef
naming conven-
tions
1637
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.
Coding Guidelines
The issue of different structure types declaring members having the same identifier spelling is discussed
elsewhere.
792 member
naming conven-
tions
1638
EXAMPLE 3 The following obscure constructions
typedef signed int t;
typedef int plain;
struct tag {
unsigned t:4;
const t:5;
plain r:5;
};
June 24, 2009 v 1.2
6.7.8 Initialization

1641
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.
Coding Guidelines
The guideline recommendation dealing with the reuse of identifiers is applicable here.
identifier
reusing names
792.3
1639
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);
Commentary
The algorithm for locating the omitted identifier in abstract declarators can be used to locate the identifier
EXAMPLE
abstract
declarators
1627
declared by the declarator in complex declarations.
1640
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];
}
Other Languages
Some languages (e.g., Ada) support parameterized typedefs that allow information, such as array size, to be
explicitly specified when the type is instantiated (i.e., when an object is declared using it).
Coding Guidelines

At the time of this writing there is insufficient experience available with how variable length array types are
used to know whether a guideline recommendation dealing with modifications to the values of objects used
in the declaration of typedef names, such as n in the above example, is worthwhile.
6.7.8 Initialization
v 1.2 June 24, 2009
6.7.8 Initialization
1641
1641
initialization
syntax
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designation
opt
initializer
initializer-list , designation
opt
initializer
designation:
designator-list =
designator-list:
designator
designator-list designator
designator:
[ constant-expression ]
. identifier
Commentary

The term designated initializer is sometimes used in discussions by members of the committee. However,
the C Standard does not use or define this term, it uses the term designation initializer.
Rationale
A new feature of C99: Designated initializers provide a mechanism for initializing sparse arrays, a practice
common in numerical programming. They add useful functionality that already exists in Fortran so that
programmers migrating to C need not suffer the loss of a program-text-saving notational feature.
This feature also allows initialization of sparse structures, common in systems programming, and allows
initialization of unions via any member, regardless of whether or not it is the first member.
C90
Support for designators in initializers is new in C99.
C
++
Support for designators in initializers is new in C99 and is not specified in the C
++
Standard.
Other Languages
Ada supports the initialization of multiple objects with a single value.
1 Total_val,
2 Total_average : INTEGER : = 0;
Ada (and Extended Pascal) does not require the name of the member to be prefixed with the
.
token and uses
the token => (Extended Pascal uses :), rather than =.
Extended Pascal supports the specification of default initial values in the definition of a type (that are then
applied to objects defined to have that type).
The Fortran DATA statement is executed once, prior to program startup, and allows multiple objects to be
initialized (sufficient values are used to initialize each object and it is the authors responsibility for ensuring
that each value is used to initialize the intended object):
June 24, 2009 v 1.2

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×