1. Trang chủ
  2. » Công Nghệ Thông Tin

The New C Standard- P14

100 360 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề The New C Standard - P14
Trường học Standard University
Chuyên ngành Computer Science
Thể loại Thesis
Năm xuất bản 2009
Thành phố Standard City
Định dạng
Số trang 100
Dung lượng 695,45 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 1

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

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 2

Coding 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 3

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;

}

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 4

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 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 5

1602The 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 6

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.”

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 7

Coding 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 8

The 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 9

Coding 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 10

with 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 11

1615If 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 12

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.)

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 13

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 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 14

EXAMPLE 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 15

Coding 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 16

direct-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 17

1627EXAMPLE 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 18

identifier 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 19

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.,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 20

1631

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 21

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)

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 22

1 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 23

1634That 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 24

declare 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 25

initialization 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 26

1 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 27

A 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 28

X 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 29

being 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 30

identifier

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 31

All 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 32

then 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 33

translator 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 34

declaration 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 35

object 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 36

The 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 37

1658The 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 38

The 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 39

The 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 40

Common 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)

Ngày đăng: 28/10/2013, 15:15

TỪ KHÓA LIÊN QUAN