an identifier can be treated as a typedef name or as a parameter name, it shall be taken as a fis declared as function returningint, taking a single parameter of type pointer to function
Trang 1spelling 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
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: 729arrayconverted to
pa-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
Trang 2Coding Guidelines
A qualifier appearing outside of[and]qualifies the element type, not the array type For non-parameterdeclarations this distinction is not significant, the possible consequences are the same However, the implicitconversion to pointer type, that occurs for parameters having an array type, means that the distinction issignificant in this case Experience shows that developers are not always aware of the consequences of thisadjustment 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:
• Thevolatilequalifier— the final effect is very likely to be the intended effect (wanting tovolatile
qualify an object having a pointer type is much rarer than applying such a qualifier to the object itpoints at)
• Theconstqualifier— 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 theiruse to know whether any guideline recommendation is cost effective
1599
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
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 theserequirements 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]);
Trang 3Other 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;
}
optimizer can use, for example, to unroll the loop and reorder the loads and stores of the elements referenced
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 732functiondesignator
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)
supplies no information
Trang 4That 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 differenttypes The origin of support for variable numbers of arguments was the desire to treat functions handlinginput/output in the same as any other function (i.e., the handling of I/O functions did not depend on specialhandling by a translator, such as what is needed in Pascal for thereadandwritefunctions)
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 developerssometimes need to define functions that were passed variable numbers of arguments the C Committeeintroduced the ellipsis notation, in function prototypes The presence of an ellipsis gives notice to a translatorthat different argument types may be passed in calls to that function Access to any of these arguments isobtained by encoding information on the expected ordering and type, via calls to library macros, within thebody of the function
Coding Guidelines
Many coding guideline documents recommend against the use of ellipsis The view being taken that use ofthis notation represents an open-ended charter for uncontrolled argument passing What are the alternativeand 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 ofoptional arguments A function could be defined to take the maximum number of arguments In thosecases where a call did not need to pass values to all the arguments available to it, a dummy argumentcould be passed The number of different argument types is also, usually, small A union type could beused to represent them
In both cases the body of the function needs some method of knowing which values to access (as it doeswhen the ellipsis notation is used)
Is the cure worse than the problem? The ellipsis notation has the advantage of not generating new interfaceissues, which the use of file scope objects is likely to do The advantage to declaring functions to take themaximum number of arguments (use of union types provides the subset of possible types that argument valuesmay have) is that information about all possible arguments is known to readers of the function definition Thebenefit 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 areassigned 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 writtencode A guideline recommendation dealing with this issue is not considered worthwhile
Trang 51602The special case of an unnamed parameter of typevoidas 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 that1608functiondeclarator
empty list
provided no information about the arguments it might take (although it might take one or more) Use of the
keywordvoidprovides 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
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
fis declared as function returningint, taking a single parameter of type pointer to function returningint
and taking a parameter of typeT Without the above rule it could also be interpreted as function returning
int, taking a single parameter of type pointer toint, with redundant parentheses around the identifierT
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 ofWG14_N852the identifierwhatis treated as a typedef name and violates
declarator return type
1 typedef int what;
2
3 int WG14_N852(int (what)(int)); /* Constraint violation */
empty parentheses
Trang 6The 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.”
1604
If the function declarator is not part of a definition of that function, parameters may have incomplete type and
5 * Cannot define an argument to have type struct T at
6 * this point so the function is not yet callable.
The following declarations offare all compatible with each other (the operand ofsizeofdoes 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
8.3.5p6 If the type of a parameter includes a type of the form “pointer to array of unknown bound ofT” or “reference to
array of unknown bound ofT,” 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 oflanguages provide some form of[*]notation to indicate parameters having a variable length array type
Trang 7Coding Guidelines
Functions declared with a parameter having an incomplete structure or union type might be regarded as
11 f_1(loc_1); /* Constraint violation, different structure type */
12 f_2(loc_2); /* Argument has same structure type as parameter */
13 }
unless the declared parameter is one of the members of the parameter type list for a function definition
Whether or not function declarations are token for token identical to their definitions is not considered
worthwhile addressing in a guideline recommendation
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++
no parameters
Commentary
For a definition of a function, for instancef, there is no difference between the formsf()andf(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 keywordvoid
are not sufficient to warrant a guideline recommendation dealing with this issue
Trang 8The following applies to both declarations and definitions of functions:
8.3.5p2 If theparameter-declaration-clauseis empty, the function takes no arguments
A call made within the scope of a function declaration that specifies an empty parameter list, that containsarguments will cause a C++translator to issue a diagnostic
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 ofthe function type
If one return type is an enumerated type and the another return type is the compatible integer type C wouldconsider 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 allowsmore than one declaration of a function, or when pointers to functions are supported In these cases therequirements specified are usually along the lines of those used by C
Trang 9Coding Guidelines
The rationale for the guideline recommendations on having a single textual declaration and including the422.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.1identifierdefinition
shall #include
declarations are compatible
the ellipsis terminator;
This requirement applies between two function declarations, not between the declaration of a function and a
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 ble type if
compati-parameter type is an enumerated type and the corresponding compati-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
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
Trang 10with the case of source code containing so called old style and function prototype declarations for the samefunction.
Calls where the visible function declaration uses an old style declaration, have their arguments promotedusing the default argument promotions The types of the promoted arguments are required to be compatible
by an implementation) are consistent
The ellipsis terminator is a special case Some translators are known to handle arguments passed to thisparameter differently than when there is a declared type (the unknown nature of the arguments sometimesmeans that this special processing is a necessity) Given the possibility of this implementation technique theCommittee 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 nothave any This issue is covered elsewhere
8.3.5p2 If theparameter-declaration-clauseis 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 objectcode file is likely to contain function signatures, which are based on the number and type of the parameters
alignment 39
usually assign parameters at least the same alignment as those of the typeint(ensuring that integer typeswith less rank are aligned on the storage boundaries of their promoted type) For processors that have morerelaxed alignment requirements, or where optimisations are possible for the smaller integer types, parametershaving a type whose rank less than that of the typeintare sometime assigned a different storage locationthan if they had a type of greater rank In this case the arguments, which will have been treated as having atleast typeint, 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 behavioraldifferences that did not previously exist Most of the benefits of function prototype usage come from thechecks 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 internallinkage 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 guidelinerecommendations apply
Trang 111615If 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 1009default 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
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#included in the source file that defines the function is not1818.1identifierdefinition
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
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
Trang 12com-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.)
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 anycomposite type In the following exampleDR_040_aandDR_040_bhave the same composite types, but theparameter 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)
was changed by the response to DR #013 question 1 (also see DR #017q15 and DR #040q1)
The C++Standard transforms the parameters’ types and then:
8.3.5p3 If astorage-class-specifiermodifies a parameter type, the specifier is deleted [Example: register
char*becomeschar*—end example] Suchstorage-class-specifiersaffect only the definition of theparameter within the body of the function; they do not affect the function type The resulting list of transformedparameter types is the function’s parameter type list
Trang 13It 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 that1810.1functiondeclaration
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 */
function ing pointer to int f(void), *fip(), (*pfi)();
necessary to indicate that indirection through a pointer to a function yields a function designator, which is then
If the declaration occurs outside of any function, the identifiers have file scope and external linkage If the
internal or external linkage (depending on what file scope declarations for these identifiers are visible), and
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++
int (*apfi[3])(int *x, int *y);
Commentary
The parentheses are necessary becauseint *apfi[3](int *x, int *y);declaresapfito be an array of
three functions returning pointer toint(which is a constraint violation) 1567 array element
not function type
Trang 14EXAMPLE 3 The declaration
int (*fpfi(int (*)(long), int))(int, );
arguments of any type
Commentary
The declaration
1 int (* (*fpfpfi(int (*)(long), int))(int, ))(void);
declares a functionfpfpfithat returns a pointer to a function returning a pointer to a function returning an
intinvolves parenthesizing part of the existing declaration and adding information on the parameters (in thiscase(void))
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
Trang 15Coding Guidelines
The expressionn*m+300occurs 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
compatible tion prototypes double maximum(int n, int m, double a[n][m]);
func-double maximum(int n, int m, func-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]);
non-null pointer to the first of at least three arrays of 5 doubles, which the others do not.)
ab-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
Trang 16direct-abstract-declarator opt [ assignment-expression opt ]
C90 only permittedconstant-expressionoptto 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 typethat 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 theadditional task of locating the position in the token sequence where the identifier would have been, had it notbeen omitted Whether there is sufficient benefit in providing an identifier (taking into account the costs ofproviding it in the first place) to make a guideline recommendation worthwhile is a complex question thatyour author does not yet feel capable of answering
is that readers of the source may have to spend additional time searching for the identifiers declared, becausethey 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
Trang 171627EXAMPLE The constructions EXAMPLE
abstract declarators
(h) int (*const [])(unsigned int, )
and (h) array of an unspecified number of constant pointers to functions, each with one parameter that has
typeunsigned intand an unspecified number of other parameters, returning anint
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/enumdeclarations), 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
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
126 type name empty paren- theses
parameter specification”, rather than redundant parentheses around the omitted identifier
Commentary
That is in the following declaration off:
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 */
Trang 18identifier as atypedef-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 symboltable) whethertype_ident(D_1);is a function call or a declaration ofD_1using redundant parentheses
There are some contexts where the status of an identifier as atypedef-namecan be deduced For instance,the token sequence; x y;is either a declaration ofyto have the type denoted byx, or it is a violation ofsyntax (because a definition ofxas a typedef name is not visible)
The syntax of C declarations and the status oftypedef-name as an identifier token creates a number
of implementation difficulties The parser either needs access to a symbol table (so that it knows whichidentifiers are defined astypedef-names), or it needs to look ahead more than one token and be able tohandle 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 orreset 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 atypedef-namein 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 theidentifier defined by a typedef declaration There is no obvious benefit in attempting to change this commondeveloper usage The issue of naming conventions for typedef names is discussed elsewhere
Trang 19the 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.,intorstruct) 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 792tagnaming
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 category553 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 tions
associa-• 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 is553 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 atypedefdefinition (as a percentage of all types appearing intypedefdefinitions).
Based on the translated form of this book’s benchmark programs.
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.1632array sizeevaluated 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
Trang 201631
name that denotes the type specified for the identifier in the way described in 6.7.5
RationaleUsing atypedefto declare a variable length array object (see §6.7.5.2) could have two possible meanings
when the object is declared For example
{ typedef int VLA[n];
n++;
VLA object;
//
}
time the type definition is used for some object declaration The Committee decided that if the evaluationwere to take place each time the typedef name is used, then a single type definition could yield variablelength array types involving many different dimension sizes This possibility seemed to violate the spirit of typedefinitions The decision was made to force evaluation of the expression at the time the type definition itself isencountered
Trang 21In 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)
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.2full declaratorall orderings give
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
typedefonly 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:
Trang 221 typedef const struct T {
Your author’s experience (based on teaching Pascal and analyzing source code written in it) is that ittakes time and practice for developers to learn how to use strong type names effectively While the conceptsbehind individual type names may be quickly learned and applied, handling the design decisions behind theinteraction between different type names requires a lot of experience Even in languages such as Pascal andAda, where implementations enforced strong typing, developers still required several years of experience toattain some degree of proficiency
Given C’s permissive type checking (e.g., translators are not required to perform much type checking onthe standard integer types), only a subset of the possible type differences are required to cause a diagnostic
standard
integer types493
to be generated Given the lack of translator support for strong type checking and the amount of practiceneeded to become proficient in its use, there is no cost/benefit in recommending the use of typedef names fortype checking purposes The cost/benefit of using typedef names for other purposes, such as enabling thetypes 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 occurmuch more frequently than the tag names (by a factor of 1.7:1; although this ratio is switched in someprograms, e.g., tag names outnumber typedef names by 1.5:1 in the Linux kernel) Why is this (and shouldthe 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 thestruct/union/enumkeyword is considered to be useful information, highlightingthe kind of type that is being used (structure types in particular seem to be regarded as very differentanimals than other types) While this rationale goes against the design principle of hiding representationdetails, 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 thestructure 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 definitiondecision process
Trang 231634That is, in the following declarations:
typedef T type_ident;
type_ident D;
type_identis defined as a typedef name with the type specified by the declaration specifiers inT(known as T ),
Commentary
A declaration of the formtype_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
Commentary
ordinary identifiers
typedef int MILES, KLICKSP();
typedef struct { double hi, lo; } range;
Coding Guidelines
The use of uppercase in typedef names is discussed elsewhere 792typedefnaming
conven-tions
typedef struct s1 { int x; } t1, *tp1;
typedef struct s2 { int x; } t2, *tp2;
Coding Guidelines
The issue of different structure types declaring members having the same identifier spelling is discussed
conven-tions
typedef signed int t;
typedef int plain;
struct tag { unsigned t:4;
const t:5;
plain r:5;
};
Trang 24declare a typedef nametwith typesigned int, a typedef nameplainwith typeint, and a structure with
bit-field which (if it could be accessed) would contain values in either the range [-15, +15] or [-16, +15], and
visible as a typedef name) If these declarations are followed in an inner scope by
t f(t (t));
long t;
On the other hand, typedef names can be used to improve code readability All three of the following
names
typedef void fv(int), (*pfv)(int);
void (*signal(int, void (*)(int)))(int);
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];
Trang 25initialization syntax
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
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 FortranDATAstatement 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):
Trang 261 CHARACTER*5 NAME
2 INTEGER I, J, K
3 DATA NAME, I, J, K / ’DEREK’, 1, 2, 3/
Ada, Extended Pascal, and Fortran (since the 1966 standard) all provide a method of specifying a range ofarray elements that are to be initialized with some value
Algol 68 uses the token=to specify that the declared identifier is a constant, and:=to specify that declaredidentifier is a variable with an initial value For instance:
1 INT c = 5; # constant #
2 INT d = c + 1; # constant #
3 INT e := d; # initialized variable #
BCPL supports parallel declarations, for instance:
1 LET a, b, c = x, y, z; // declare a, b, and c, and then in some order assign x to a, y to b, and z to c
Common Implementations
Some prestandard implementations (e.g.,pcc) parsed initializers bottom-up, instead of top-down (as required
by the standard) A few modern implementations provide an option to specify a bottom-up parse (e.g., theDiab Data C compiler[359]supports the-Xbottom-up-initoption) For instance, in:
Dev825.3
An integer constant may appear in the list of initializers for an object having an array or structure type
Trang 27A floating constant may appear in the list of initializers for an object
What are the costs and benefits of organizing the visible appearance of initializers in different ways (like
other layout issues, experience suggests that developers have an established repertoire of layout rules which 1707 statement
visual layout
provide the template for laying out individual initializers, e.g., a type-based layout with array elements in
columns and nested structure types indented like nested conditionals)? 1348 declaration
visual layout
• Cost— the time taken to create the visual layout and to maintain it when new initializers are added, or
existing ones removed Experience suggests that developers tend to read aggregate initializers once
(to comprehend what their initial value denotes) and ignore them thereafter Given this usage pattern,
initializers are likely to be a visual distraction most of the time (another cost)
• Benefit— the visual layout may reduce the effort needed by readers to comprehend them
Until more is known about the frequency with which individual initializers are read for comprehension,
as opposed to being given a cursory glance (almost treated as distractions) it is not possible to reliably
provide cost-effective recommendations about how to organize their layout The following discusses some
possibilities
The gestalt principle of organization suggest that related initializers be visually grouped together It is not770 gestalt
princi-ples
always obvious what information needs to be visually grouped For instance, for an array of structure type
initializer might be grouped by array element or by structure member, or perhaps an application oriented
Other considerations on initializer layout include making it easy to check that all required initializers are
present and visually exposing patterns in the constants appearing in the initializer (with the intent of reducing
the effort needed, by subsequent, to deduce them):
1 int bit_pattern[] = {
2 0001, /* octal constant */
Trang 28X X
X O O O
Q
Q Q
Q O
Counting O’s in X distractors
0 400 1200 2000 2800 3600
Figure 1641.1: Average time (in milliseconds) taken for subjects to enumerate O’s in a background of X or Q distractors Based
on Trick and Pylyshyn [1397]
5 1000, /* Context may cause reader treat this as an octal constant */
Other cognitive factors include subitizing and the Stroop effect
When people are asked to enumerate how many dots, for instance, are visible in a well defined areasubitizing
their response time depends on the number of dots However, when there are between one and four dotsperformance varies between 40 ms to 100 ms per dot With five or more dots performance varies between
250 ms to 350 ms per dot The faster process used when there are four or fewer dots is called subitizing(people effortlessly see the number of dots), while the slower process is called counting
Subitizing has been shown[1397]to rely on information available during the preattentive stage of vision
vision
preattentive770
Items that rely on later stages of visual processing (e.g., those requiring spatial attention, such as enumeratingthe number of squares along a given line) cannot be subitized, they have to be counted The limit on themaximum number of items that can be subitized is thought to be caused by capacity limits in the preattentivestages of vision.[1398]The extent to which other items, distractors, visible on the display reduce enumerationperformance depends on the number of distractors and whether it is possible to discriminate between thevisible items during the visual systems preattentive stage For instance, it is possible to subitize the letter Owhen the distractors are the letter X, but not when the distractors are the letter Q (see Figure1641.1)
A study by Stroop[1335]asked subjects to name the color of ink that words were written in For instance,stroop effect
the word red was printed in black, blue, and green inks and the word blue was printed in black, red, and greeninks The results showed that performance (response time and error rate) suffered substantial interferencefrom the tendency to name the word, rather than its color
The explanation for what has become known as the Stroop effect is based on interference, in the humanbrain, between the two tasks of reading a word and naming a colour (which are performed in parallel; ingeneral words are read faster, an automatic process in literate adults, than colors can be named) Whether theinterference occurs in the output unit, which is serial (one word arriving just before the other and people only
Trang 29being able to say one word at a time), or occurs between the units doing the naming and reading, is still an
open question
Experiments using a number of different kinds of words and form of visual presentation have replicated
the effect For instance, the Stroop effect has been obtained using lists of numbers Readers might like to try
counting the number of characters occurring in each separate row appearing in the margin 3 3 3 3
Studies[1083]have found that when subjects are asked to enumerate visually presented digits, the amount
of Stroop-like interference depends on the arithmetic difference between the magnitude of the digits used and
the number of those digits displayed Thus a short, for instance, list of large numbers is read more quickly
and with fewer errors than a short list of small numbers Alternatively a long list of small numbers (much
smaller than the length of the list) is read more quickly and with fewer errors than a long list of numbers
where the number has a similar magnitude to the length of the list
Initializers often contain lists of similar numbers The extent to which initializer layout interacts with
readers using subitizing/counting and the Stroop effect is not known
Table 1641.1: Occurrence of object types, in block scope, whose declaration includes an initializer (as a percentage of the type of
all such declarations with initializers) Based on the translated form of this book’s benchmark programs Usage information on
the types of all objects declared at file scope is given elsewhere (see Table 1348.2 ).
other-types 9.1 unsigned short 2.4
Table 1641.2: Occurrence of object types with internal linkage, at file scope, whose declaration includes an initializer (as a
percentage of the type of all such declarations with initializers) Based on the translated form of this book’s benchmark programs.
Usage information on the types of all objects declared at file scope is given elsewhere (see Table 1348.4 ).
Trang 30identifier
1648
C90
There shall be no more initializers in an initializer list than there are objects to be initialized
Support for designators in initializers is new in C99 and a generalization of the wording is necessary to coverthe case of a name being used that is not a member of the structure or union type, or an array index that doesnot lie within the bounds of the object array type
There is no mechanism for specifying a repeat factor for initializers in C, the number of elements is known
at translation time By the nature of their intended usage the number of elements in an object having avariable length array type is not known at translation time and is likely to vary between different instances oftype instantiation during program executions Providing support for initializations would involve specifyingmany different combinations of events, a degree of complexity that is probably not worth the cost It is notpossible to specify a single initial value, in the declaration of an object having a variable length array type, as
a means of implicitly causing all other elements to be initialized to zero
Trang 31All the expressions in an initializer for an object that has static storage duration or in an initializer list for an
object that has aggregate or union type shall be constant expressions
C99 has relaxed the requirement that aggregate or union types always be initialized with constant expressions
Support for string literals in this context was added by the response to DR #150
C++
8.5p2
Automatic, register, static, and external variables of namespace scope can be initialized by arbitrary expressions
involving literals and previously declared variables and functions
A program, written using only C constructs, could be acceptable to a conforming C++implementation, but
not be acceptable to a C implementation
C++translators that have an operate in C mode option have been known to fail to issue a diagnostic for
initializers that would not be acceptable to conforming C translators
Other Languages
Not all languages require the values of initializers to be known at translation time
linkage at block scope
declaration shall have no initializer for the identifier
Commentary
Existing code, prior to C90, contained declarations of identifiers with external linkage (but without initializers)
in block scope and the C committee sanctioned its continued use Providing an initializer for an object having
external or internal linkage, is one method of specifying that it denotes the definition of that object However,1354 object
reserve storage
support for such usage has no obvious benefit in block scope
Block scope definitions that include thestaticstorage-class specifier have no linkage and may contain435 no linkage
block scope object
an initializer
C++
The C++Standard does not specify any equivalent constraint
Coding Guidelines
If the guideline recommendation dealing with declaring identifiers with external or internal linkage at file
declared in one file
expression
constant-[ constant-expression ]
then the current object (defined below) shall have array type and the expression shall be an integer constant
expression
Commentary
While it is possible for the initialization value to be a nonconstant, the designator of the element being
initialized must be constant Requiring support for nonconstant designators would have introduced a number
of complexities For instance, in the following example:
1 int n;
2 /* */
3 int vec_1[] = { [n] = 2 };
4 int vec_2[10] = { [n] = 2 };
the declaration ofvec_1is essentially a new way of specifying a VLA, while in the initializer forvec_2a
translator cannot enforce the constraint that initializers only provide values for objects contained within the
entity being initialized until program execution 1642value not con-initializer
tained in object
Trang 32then the current object (defined below) shall have structure or union type and the identifier shall be the name
of a member of that type
The machine code generated to initialize scalar objects with automatic storage duration is usually the same
as that used to assign a value in an expression statement
It is much easier to generate efficient machine code for aggregate objects, with automatic storage duration,when an initializer is used compared to when the developer has used a sequence of assignment statements (the
Trang 33translator does not need to perform any analysis to deduce that the values being assigned are all associated
with the same object) Using compound literals is likely to result in translators allocating additional storage
for the unnamed object (unless it can be deduced that such storage is unnecessary)
1058 compound literal unnamed ob- ject
Coding Guidelines
Some coding guideline documents recommend that an initializer always be used to store an initial value in an
object The rationale being that providing an initial value in the definition of the object guarantees that the
object is always initialized before use (the concern seems to be more oriented towards ensuring an object
does not have an indeterminate value than that it have the correct value, which it might not be possible to
assign at the point of definition)
One potential advantage, in providing an initializer, is that if the definition is moved from an inner to an
outer scope (moving from an outer to an inner scope will cause a diagnostic to be issued for any references
from outside the new scope), during program modification, its initialization is moved at the same time (if the
initial value is assigned in a statement, then that statement also needs to be moved; forgetting to do this is a
potential source of faults) The following lists several potential disadvantages to this usage:
• The values of the operands in the expression providing the initial value may not be correct, at the point
in the source where the initializer occurs It is also possible that the object represents some temporary
value that depends on the value of other objects defined in the same scope
• A strong case can be made for initializing objects close to their point of use, so the associated source
forms a readily comprehensible grouping (this is also an argument for moving the definition closer to
its point of use) This issue is discussed in more detail elsewhere 1707 statement
syntax
• Use of an initializer has been found to sometimes give developers a false sense of security, causing the
assignment of a value to be overlooked For instance, when two very similar sequences of source code
occur in a function the second is often created by modifying a copy of the first one A commonly seen
mistake, when initializers are used, is to forget to give some object a new initial value
In C99 it is possible to intermix declarations and statements This means that the point of declaration, of an
object, could be moved closer to where it is first used The issue of where to declare objects is discussed
close to usage
There is no evidence to suggest that the benefits of unconditionally providing an initializer in the definition
of objects are greater than the costs For this reason no guideline recommendation is given
Trang 34declaration of such an object is encountered during program execution).
1 extern _Bool g(void);
2
3 void f (void)
5 loop: ;
6 int i = 42; /* Explicit initialization occurs every time declaration is encountered */
7 int j; /* as does implicit initialization to indeterminate value */
8 if (g()) /* j always has an indeterminate value here */
The behavior resulting from reading the value of an object having an indeterminate value depends on the type
of the object Objects having typeunsigned charare guaranteed to contain a representable value (or an
unsigned
char
pure binary
571
Trang 35object having type array ofunsigned char) In this case a read access results in unspecified behavior The
indeterminate value of objects having other types may be a trap representation (accessing an object having 581 trap
repre-sentation
such a value results in undefined behavior)
Other Languages
Some languages (e.g., Perl) implicitly assign a value to all objects before they are explicitly assigned to (in
the case of Perl this is the valueundef, which evaluates to either 0 or the zero length string) Some languages
(e.g., Ada and Extended Pascal) allow the definition of a type to include an initial value that is implicitly
assigned to objects defined to have that type
Common Implementations
The value of such objects is usually the bit pattern that happened to exist in the storage allocated at the start
its lifetime This bit pattern may have been created by assigning a value to an object whose lifetime has
ended, or the storage may have been occupied by more than one object, or it may have been used to hold
housekeeping information A few implementations zero the storage area, reserved for defined objects, on
function entry
Example
1 { float f=1.234; }
2 { int i; /* i will probably be allocated storage previously occupied by f */ }
default value
Commentary
In the past many implementations implicitly initialized the storage occupied by objects having static storage
duration to all bits zero prior to program startup This practice is codified in the C Standard here; where the
intent of all bits zero, i.e., a value representation of zero, is specified The issue of initializing objects with
static storage duration is discussed elsewhere
151 static storage duration initialized before startup
456 static storage duration when initialized
Figure 1652.1: Number of object declarations that include an initializer (as a percentage of all corresponding object declarations),
either within function definitions (functions that did not contain any object definitions were not included), or within translation
units and having internal linkage (while there are a number of ways of counting objects with external linkage, none seemed
appropriate and no usage information is given here) Based on the translated form of this book’s benchmark programs.
Trang 36The memory occupied by any object of static storage duration shall be zero-initialized at program startup beforeany other initialization takes place [Note: in some cases, additional initialization is done later ]
Zero is the constant most frequently assigned to an object and the most commonly occurring constant literal
in source code It is the initial value most likely to be chosen by a developer, if one had to be explicitly
at the bottom and working up) It is also less likely to cause maintenance problems (since developers tend toadd new members at the end of the list of current members)
If the first named member is an aggregate all the members of that aggregate are initialized
Trang 371658The initializer for a scalar shall be a single expression, optionally enclosed in braces initializer
scalar
Commentary
Allowing initializers for scalars to be enclosed in braces can simplify the automatic generation of C source
(the generator does not need any knowledge of the type being initialized, it can always output braces) Braces
are not optional when a value appears as the right operand of an assignment operator
If the conversion cannot be done, the initialization is ill-formed
While a C++translator is required to issue a diagnostic for a use of this ill-formed construct, such an
occurrence causes undefined behavior in C (the behavior of many C translators is to issue a diagnostic)
The C90 wording did not include “(after conversion)” Although all known translators treated initializers just
like assignment and performed the conversion
an object having a const-qualified type to be assigned a value A consequence of taking the unqualified
type is that initialization does not have exactly the same semantics as simple assignment if the object has a 1303 simple
as-signment
volatile-qualified type
Trang 38The initializer for a structure or union object that has automatic storage duration shall be either an initializer list
as described below, or a single expression that has compatible structure or union type
Commentary
Just like simple assignment, it is possible to initialize a structure or union object with the value of anotherobject having the same type In the case of a union object the value assigned need not be that of the firstnamed member
C++
The C++Standard permits an arbitrary expression to be used in all contexts (8.5p2)
This difference permits a program, written using only C constructs, to be acceptable to a conforming C++
implementation but not be acceptable to a C implementation C++translators that have an operate in C modeswitch do not always diagnose initializers that would not be acceptable to all conforming C translators
1663
In the latter case, the initial value of the object, including unnamed members, is that of the expression
initializing
including
un-named members Commentary
The former case is the subject of discussion of most of the rest of the C subclause There is one case whereunnamed members participate in initialization
Trang 39The C++Standard does not contain this specification.
array of char
Commentary
A string literal is represented in storage as a contiguous sequence of characters, an array is a contiguous
sequence of members This specification recognizes parallels between them The rationale for permitting
scalar
Coding Guidelines
What are the cost/benefit issues of expressing the initialization value using a string literal compared to
expressing it as a comma separated list of values? The string literal form is likely to have a more worthwhile
cost/benefit when:
• the value is likely to be familiar to readers as a character sequence (for instance, it is a word or
sentence) There is a benefit in making use of this existing reader knowledge, or
• the majority of the individual characters in the string literal are represented in the visible source using
characters, rather than escape sequences, the visually more compact form may require less effort, from
a reader, to process
One situation where use of a string literal may not be cost effective is when the individual character values are
used independently of each other For instance, they represent specific data values or properties In this case
it is possible that macro names have been defined to represent these values Referencing these macro names
in the initializer eliminates the possibility that a change to their value will not be reflected in the initializer
uses sive characters from string literal
succes-if the array is of unknown size) initialize the elements of the array
An object initialized with a string literal whose terminating null character is not included in the value used to
initialize the object, will cause a diagnostic to be issued by a C++translator
1 char hello[5] = "world"; /* strictly conforming */
Trang 40Common Implementations
Some implementations store the string literal as part of the program image and copy it during initialization.Others generate machine code to store constants (the individual character values, often concatenated to formlarger constants, reducing the number of store instructions) into the object
Coding Guidelines
Developers sometimes overlook a string literal’s terminating null character in their calculation of the number
of array elements it will occupy (either leaving insufficient space to hold the null character, when an arraysize is specified, or forgetting that storage will be allocated for one, in the case of an incomplete array type).There is no obvious guideline recommendation that might reduce the probability of a developer making thesekind of mistakes
Example
1 char a_1[3] = "abc", /* storage holds | a | b | c | */
2 a_2[4] = "abc", /* storage holds | a | b | c | 0 | */
3 a_3[5] = "abc", /* storage holds | a | b | c | 0 | 0 | */
4 a_4[] = "abc"; /* storage holds | a | b | c | 0 | */
In (assuming any undefined behavior does not terminate execution of the program):
The cost/benefit of using a wide string literal in the visible source, rather than a comma separated list, will
be affected by the probability that the wide characters will appear, to a reader, as a glyph rather than somemultibyte sequence (or other form of encoding)
Other Languages
Many languages support this form of initializer (although the delimiters are not always braces)