C, Ocean Edition I

6.7 Declarations

Clause
6.7
ISO C99 mapping
6.7
Status
Normative

6.7.0 General

declaration:
	declaration-specifiers init-declarator-list-opt ;

declaration-specifiers:
	storage-class-specifier-opt type-qualifier-list-opt function-specifier-opt type-specifier

init-declarator-list:
	init-declarator
	init-declarator-list , init-declarator

init-declarator:
	declarator
	declarator = initializer

A declaration introduces the existence and the type of an entity.

A definition additionally creates the entity or supplies its body.

For objects there are two categories:

definition
external declaration

For functions there are two categories:

declaration
definition

There is no third category. Tentative definitions do not exist. See clause 6.9.2 and behavior.md section 10.

Constraints.

A declaration shall declare at least a declarator, a tag, or the members of an enumeration.

An identifier shall not be declared twice in the same scope in the same name space, except where clause 6.9.5 permits compatible redeclaration.

ISO C99 mapping: 6.7.


6.7.1 Storage-Class Specifiers

storage-class-specifier:
	typedef
	extern
	static

At most one storage-class specifier may appear in a declaration.

Difference: ISO C99 also has auto and register. Both are removed. See clause 6.4.1.1.

6.7.1.1 typedef

typedef declares the identifier as a name for a type rather than as an object or a function. See clause 6.7.7.

It occupies the storage-class specifier position for grammatical reasons that predate the edition. It is not a storage class in any other sense.

6.7.1.2 static

static binds persistent identity locally rather than participating in the surrounding lifetime or linkage rules. It has two accepted uses, which are two views of that single idea.

At file scope:

static int counter;

The entity has internal linkage. Its name is not visible to other translation units.

At block scope:

void count(void)
{
	static int counter;
	counter++;
}

The object has static storage duration, so it persists across invocations of the function. Its name has no linkage.

Uses that are not retained.

static inside array parameter brackets is not accepted:

void process(int values[static 16]);   /* invalid */

There is no third life for static as an array-parameter contract. Write the type that is actually being passed. See clause 6.7.5.2 and syntax.md section 12.

Design note. The two retained meanings are a genuine irregularity, and the edition tolerates it. Replacing the spelling would make ordinary source stop looking like C, which is a worse outcome than the irregularity. This is the litmus test of syntax.md section 59.6 producing a decision to leave something alone.

6.7.1.3 extern

extern means one thing: this declaration refers to an entity defined elsewhere.

extern int counter;         /* declaration */

An extern declaration shall not have an initializer:

extern int counter = 10;    /* constraint violation */

An extern declaration does not reserve storage and does not define an entity. See clause 6.2.2.4.

6.7.1.4 static const composition

A declaration such as:

static const int max_users = 64;

composes independent properties:

static   local persistent identity
const    cannot be modified after initialization
int      object type

There is no separate category named "static constant". The meaning is the composition of the parts, and the parts mean what they mean everywhere else. Such an object is usable as an integer constant expression under clause 6.6.6. See syntax.md section 13.

ISO C99 mapping: 6.7.1.


6.7.2 Type Specifiers

type-specifier:
	void
	bool
	char
	signed char
	unsigned char
	short
	unsigned short
	int
	unsigned int
	long
	unsigned long
	long long
	unsigned long long
	float
	double
	long double
	struct-or-union-specifier
	enum-specifier
	typedef-name

6.7.2.1 Canonical primitive spellings

The spellings listed above are the canonical spellings, and they are the only spellings of the primitive types.

A spelling that conveys no information the canonical spelling does not already convey is a constraint violation. The implementation shall diagnose it and shall name the canonical form.

Rejected Canonical
signed int
signed int int
unsigned unsigned int
signed short short
short int short
signed short int short
unsigned short int unsigned short
signed long long
long int long
unsigned long int unsigned long
long long int long long
unsigned long long int unsigned long long
long unsigned unsigned long
int unsigned long unsigned long
_Bool bool

The principle is stated in syntax.md section 19: if two type spellings convey identical information, retain one.

The order of the words within a canonical spelling is fixed. unsigned long is the spelling, and long unsigned is not an alternative arrangement of it.

Note. signed char is canonical and is not redundant, because plain char is not signed. See clause 6.2.6.3.

Note. _Bool is rejected in favor of bool by the same rule that rejects long int in favor of long. The two spellings name one type, so the edition keeps one. See clause 6.4.1.3.

Migration. The rewrite is mechanical, and an implementation shall offer it as a suggested fix in the diagnostic.

6.7.2.2 Structure and union specifiers

struct-or-union-specifier:
	struct-or-union identifier-opt { struct-declaration-list }
	struct-or-union identifier

struct-or-union:
	struct
	union

struct-declaration:
	specifier-qualifier-list struct-declarator-list ;

struct-declarator:
	declarator
	declarator-opt : constant-expression

Structures.

A structure is an aggregate of independently addressable members, laid out in declaration order:

struct Point
{
	int x;
	int y;
};

Members occupy increasing addresses in declaration order. The implementation inserts padding as the target ABI requires, and the layout for a given target is deterministic. See clause 6.2.6.1.

A structure shall have at least one member. A structure with no members is a constraint violation.

Structure assignment copies the complete value, including padding.

Unions.

A union is overlapping typed storage:

union Value
{
	float f;
	uint32_t u;
};

Every member begins at the same address. The size of the union is at least the size of its largest member, rounded up to satisfy its alignment.

Writing one member and reading another reinterprets the stored representation through the type of the member read. This is defined. There is no hidden active member. See clause 6.5.2.3 and behavior.md section 22.

Bitfields.

struct Flags
{
	unsigned int ready : 1;
	unsigned int mode : 3;
};

A bitfield member shall have type bool or an integer type. Its width shall be an integer constant expression whose value is nonnegative and does not exceed the width of the declared type.

A bitfield declared with a signed integer type is signed. A bitfield declared with an unsigned integer type is unsigned. A named bitfield declared bool holds false or true, and its width shall be 1, since a wider one could hold no value the type does not already have.

Difference: ISO C99 makes the signedness of a plain int bitfield implementation-defined. Here int means signed, everywhere, including in a bitfield.

An unnamed bitfield of width zero forces the next member to begin at the next allocation unit boundary as defined by the ABI. An unnamed bitfield of nonzero width reserves bits without providing a member.

The physical layout of bitfields is determined completely by the selected target ABI, which shall define:

Once an ABI is selected, no implementation freedom remains. The problem with bitfields was never the feature; it was leaving programmers unsure what the target would produce. See syntax.md section 50 and behavior.md section 23.

A bitfield is not a portable external data format unless the chosen ABI says so.

The address of a bitfield cannot be taken. See clause 6.5.3.2.

Flexible array members.

The last member of a structure with more than one member may be an array with no element count:

struct Packet
{
	size_t length;
	unsigned char payload[];
};

The flexible member contributes no element count to the size of the containing structure. It refers to trailing storage belonging to the complete allocated object.

sizeof(struct Packet) is the size of the structure as though the flexible member were absent, including any padding the ABI places before it.

Accessing payload[i] is defined when the complete object was allocated with at least sizeof(struct Packet) + (i + 1) * sizeof(unsigned char) bytes, and is unsafe memory behavior otherwise.

A structure containing a flexible array member shall not be a member of another structure and shall not be an element of an array.

Assignment of a structure containing a flexible array member copies only the fixed portion.

Anonymous members.

Anonymous structures and unions are not part of the core edition. An implementation may provide them as a documented extension.

ISO C99 mapping: 6.7.2.1.

6.7.2.3 Enumeration specifiers

enum-specifier:
	enum identifier-opt { enumerator-list }
	enum identifier-opt { enumerator-list , }
	enum identifier

enumerator:
	enumeration-constant
	enumeration-constant = constant-expression

An enumeration declares a distinct integer type:

enum State
{
	STATE_IDLE,
	STATE_RUNNING,
	STATE_STOPPED
};

Enumerator values.

An enumerator with = has the value of its constant expression. An enumerator without = has the value of the previous enumerator plus one, or zero if it is the first.

Type of an enumerator.

An enumeration constant has the enumerated type, not int:

STATE_IDLE is a value of enum State

Compatible integer type.

Each enumerated type has a compatible integer type, chosen deterministically as the first type in this list that can represent every enumerator value:

int
unsigned int
long
unsigned long
long long
unsigned long long

Difference: ISO C99 lets the implementation choose among char, a signed integer type, and an unsigned integer type. Ocean Edition I fixes the choice, so that the size and the signedness of an enumerated type are determined by the source rather than by the compiler.

Range.

An object of enumerated type may hold any value representable in its compatible integer type. Enumerators are names, not a closed runtime set:

enum State state = (enum State)100;    /* valid */

Ordinary integer conversions apply where a program needs them, so an enumerated value may be used in arithmetic and an integer may be converted to an enumerated type.

An implementation should diagnose a comparison between values of two different enumerated types, and should diagnose a switch over an enumerated type that omits a named enumerator and has no default label.

ISO C99 mapping: 6.7.2.2.

6.7.2.4 Tags

A tag names a structure, a union, or an enumeration in the tag name space.

struct Node;                 /* declares an incomplete type */
struct Node { int value; };  /* completes it */

A structure or union type is incomplete until its closing brace, at which point it is complete. Within its own definition, a structure may contain a pointer to itself.

Two declarations of the same tag in the same scope refer to the same type. A tag declared in an inner scope declares a new type that hides the outer one.

ISO C99 mapping: 6.7.2.3.


6.7.3 Type Qualifiers

type-qualifier:
	const
	volatile
	restrict

6.7.3.1 const

An object declared const cannot be modified after initialization.

const int limit = 10;

Modifying such an object is invalid regardless of the access path used. Casting the qualifier away does not create permission:

*(int *)&limit = 20;      /* invalid; diagnosed where provable */

An implementation shall diagnose a modification of a const object through a cast when it can prove that the target object is const. Where it cannot prove it, the modification is unsafe memory behavior, and an implementation is free to place const objects in read-only storage.

Pointer to const.

Qualification on a pointed-to type restricts the access path rather than the object:

int value = 10;
const int *p = &value;

value is mutable, because value itself is not const. p cannot be used to modify it. Writing *(int *)p = 20 is valid here, because the object is not const, although an implementation should still warn that the cast discards a qualifier.

Two concepts, stated plainly:

const object
	the object itself is immutable

pointer to const
	this access path cannot perform mutation

No folklore is required. See syntax.md section 14 and behavior.md section 49.

6.7.3.2 Qualifier composition

Qualifiers may be combined. A qualifier appearing twice in the same specifier list is a constraint violation rather than a silent duplicate.

For a derived type, qualifiers apply to the type the declarator forms:

const int *p;         /* pointer to const int; p is mutable */
int *const q;         /* const pointer to int; the pointee is mutable */
const int *const r;   /* const pointer to const int */

A qualified type has the same representation, size, and alignment as its unqualified version.

Qualifiers on a structure or union type apply to every member recursively, so a const structure has no modifiable members.

An array whose element type is qualified is itself qualified in the same way. A const int [4] is an array of const int.

6.7.3.3 restrict

restrict shall qualify a pointer to an object type.

restrict states an explicit non-aliasing promise: during the execution of the block in which the restricted pointer is declared, storage reachable through that pointer is accessed through that pointer and through pointers derived from it, and not through any other independent access path that modifies it.

void blend(
	float *restrict dst,
	const float *restrict src,
	size_t count
);

Ordinary pointers carry no such promise. An implementation shall not infer non-aliasing from anything except a proof, a restrict contract, or a documented target property. See clause 6.5.0.4 and clause 4.7.

Violating the contract.

A program that violates a restrict contract has unsafe memory behavior. The implementation is entitled to have relied on the promise. This is the one place in the edition where a source-level assertion carries that weight, and it is deliberate: the optimization right is written down, in the source, where a reader can see it.

Design note. The specification states the contract directly rather than through pointer-association and effective-type terminology. A programmer should be able to read the promise they are making without a committee glossary. See syntax.md section 37 and behavior.md section 51.

6.7.3.4 volatile

volatile means one thing: every language-level access to the object is externally observable and shall actually occur.

An implementation shall not remove a volatile read, shall not remove a volatile write, shall not merge two volatile accesses into one, shall not invent an access that the program did not write, and shall not reorder a volatile access with respect to another volatile access.

volatile uint32_t *status = (volatile uint32_t *)0x40021000u;

while ((*status & READY) == 0)
{
	/* the read happens on every iteration */
}

What volatile does not mean.

volatile does not imply any of the following:

Concurrency primitives are separate, and an implementation that provides them shall document their ordering. See clause 5.1.2.4 and syntax.md section 38.

ISO C99 mapping: 6.7.3.


6.7.4 Function Specifiers

function-specifier:
	inline

Constraints.

inline shall appear only in the declaration or definition of a function, and shall appear only together with static.

The following forms are constraint violations:

inline int f(int x) { ... }          /* bare inline */
extern inline int g(int x) { ... }   /* extern inline */

The accepted form is:

static inline int min(int a, int b)
{
	return a < b ? a : b;
}

Semantics.

inline expresses three properties that a header author actually wants: translation-unit-local identity from static, a complete definition suitable for a header, and a statement that the function is inexpensive enough to inline.

Whether any call is physically inlined is an implementation decision, always. An implementation may inline a function that is not declared inline, and may decline to inline a function that is.

An externally visible function is simply a function:

int process(int value)
{
	...
}

The implementation may inline it within its translation unit, and may inline it across translation units where it performs whole-program analysis.

Difference: ISO C99 defines a matrix of inline definitions, external definitions, and linkage interactions that is more complicated than the operation it describes. Ocean Edition I keeps the useful idiom and discards the matrix. See syntax.md section 17 and behavior.md section 52.

ISO C99 mapping: 6.7.4.


6.7.5 Declarators

declarator:
	pointer-opt direct-declarator

direct-declarator:
	identifier
	( declarator )
	direct-declarator [ constant-expression-opt ]
	direct-declarator ( parameter-type-list )

pointer:
	* type-qualifier-list-opt
	* type-qualifier-list-opt pointer

C's declarator model is retained without change. A declarator is read outward from the identifier, with [] and () binding tighter than *.

int (*handler)(const char *);        /* pointer to function */
int (*handlers[8])(const char *);    /* array of 8 pointers to function */
int (*matrix)[16];                   /* pointer to array of 16 int */

These are not duplicate historical spellings. They are the consequence of the declarator model, and each says something different. The readability tool is the one C already has:

typedef int handler_fn(const char *);

handler_fn *handlers[8];

Design note. Unusual syntax is not automatically inconsistent syntax. The edition removes competing interpretations, not difficult ones. See syntax.md section 20.

6.7.5.1 Pointer declarators

* forms a pointer to the type formed by the rest of the declarator. Qualifiers written after * qualify the pointer, and qualifiers written in the specifier list qualify the pointee. See clause 6.7.3.2.

A pointer to an incomplete type is permitted. A pointer to a function is permitted.

6.7.5.2 Array declarators

Constraints.

The element type shall be a complete object type, and shall not be a function type.

The element count shall be an integer constant expression with a value greater than zero.

The count may be omitted only in these two situations:

Variable-length arrays are not part of this edition.

int values[count];       /* invalid when count is not a constant expression */
int values[64];          /* valid */

Removing them removes a group of behaviors that lived inside declaration syntax:

Runtime-sized storage is written with an allocation and a pointer, where the cost and the failure path are visible. See syntax.md section 42 and behavior.md section 27.

Array parameters are not accepted.

A parameter shall not be declared with array syntax:

void process(int values[]);       /* invalid */
void process(int values[16]);     /* invalid */

Traditional C accepts both and silently rewrites them into int *values, so the declaration says one thing and the type is another.

Write the type being passed:

void process(int *values, size_t count);   /* a pointer to elements */
void process(int (*values)[16]);           /* a pointer to an array of 16 */

Qualifiers and static inside the brackets of a parameter declaration are not accepted either, since the construct they modify is not accepted.

See syntax.md section 24 and behavior.md section 26.

6.7.5.3 Function declarators

Every function declaration is a prototype.

parameter-type-list:
	void
	parameter-list
	parameter-list , ...

parameter-declaration:
	declaration-specifiers declarator
	declaration-specifiers abstract-declarator-opt

Constraints.

A function declarator shall have a parameter type list. An empty parameter list is a constraint violation:

int read();               /* invalid */
int read(void);           /* zero parameters */
int read(void *buffer, size_t count);
int printf(const char *format, ...);

A parameter shall not have array type. A parameter shall not have function type; write a pointer to function. A parameter shall not have an incomplete type, other than through a pointer. A parameter shall not be void except as the single specifier void denoting an empty list.

An ellipsis shall be preceded by at least one named parameter.

The return type shall not be an array type and shall not be a function type. It may be any complete object type, a pointer type, or void.

Semantics.

Every function declaration completely describes the function's callable type. There is no second function type hiding behind empty parentheses, and there is no reconciliation rule between prototyped and unprototyped declarations, because the second kind does not exist. See clause 6.2.7 and syntax.md section 8.

A parameter name is optional in a declaration and required in a definition.

Parameters are objects with automatic storage duration. Their lifetime begins on entry to the function, and they are initialized with the converted argument values. A parameter may be modified within the function, which changes the local object only.

Old-style definitions do not exist.

int add(a, b)             /* invalid */
	int a;
	int b;
{
	return a + b;
}

Only the modern form exists:

int add(int a, int b)
{
	return a + b;
}

See syntax.md section 9 and behavior.md section 36.

ISO C99 mapping: 6.7.5.


6.7.6 Type Names

type-name:
	specifier-qualifier-list abstract-declarator-opt

A type name is a declaration of an object of that type with the identifier omitted. It appears in casts, in sizeof, and in compound literals.

int                      /* int */
int *                    /* pointer to int */
int [16]                 /* array of 16 int */
int (*)(int, int)        /* pointer to function */
int (*[8])(const char *) /* array of 8 pointers to function */

A type name in a cast shall not denote an array type or a function type. See clause 6.5.4.

ISO C99 mapping: 6.7.6.


6.7.7 Type Definitions

A declaration whose storage-class specifier is typedef declares the identifier as a name for the type the declarator forms.

typedef unsigned long index_t;
typedef int handler_fn(const char *);
typedef struct Point Point;

A typedef is a pure alias. It does not create a nominally distinct type. index_t and unsigned long are the same type, are compatible in every context, and are interchangeable.

An implementation should not diagnose a mismatch between an alias and its underlying type, because there is no mismatch.

A typedef name is an ordinary identifier and occupies the ordinary name space, so it can be hidden by an inner declaration.

Parsing. Interpreting a typedef name requires the parser to have declaration information available. That requirement is accepted. Compiler inconvenience is not itself a language defect, and the edition does not sacrifice recognizable C syntax to make a parser theoretically prettier. See syntax.md section 21.

ISO C99 mapping: 6.7.7.


6.7.8 Canonical Declaration Form

Declaration components appear in one conceptual order:

storage
qualifiers
function specifier
type
declarator

The function specifier position holds inline, which appears only together with static under clause 6.7.4, so the full form of a function declaration specifier list is static inline followed by any qualifiers and the type.

static const unsigned long flags;
extern volatile unsigned char status;
static inline int min(int a, int b);

Constraints.

A declaration whose specifiers appear in another order is a constraint violation. The implementation shall diagnose it and shall show the canonical arrangement.

const static int a;        /* invalid; write: static const int a; */
int static b;              /* invalid; write: static int b; */
unsigned static long c;    /* invalid; write: static unsigned long c; */

This changes no expressive power. It means that a reader and a parser no longer need to accept every historical arrangement of the same words, and that two declarations of the same thing look the same. See syntax.md section 18 and behavior.md section 11.

ISO C99 mapping: 6.7, canonicalized.


6.7.9 Initialization

initializer:
	assignment-expression
	{ initializer-list }
	{ initializer-list , }

initializer-list:
	designation-opt initializer
	initializer-list , designation-opt initializer

designation:
	designator-list =

designator:
	[ constant-expression ]
	. identifier

6.7.9.1 General

An initializer specifies the initial value of an object.

For an object with static storage duration, the initializer shall be a constant expression under clause 6.6.5, and the initialization occurs before program startup.

For an object with automatic storage duration, the initializer may be any expression, and the initialization occurs when the declaration is reached in execution.

6.7.9.2 Default initialization

Every object begins its lifetime with a valid value.

If no initializer is written, the object is zero-initialized. This applies to every storage duration, not only to static storage.

int count;               /* 0 */
double value;            /* positive zero */
int *pointer;            /* a null pointer, equal to null */
bool flag;               /* false */
enum State state;        /* the value with representation zero */
struct Point point;      /* every member recursively zero-initialized */
int values[8];           /* every element zero-initialized */

Zero initialization of a structure or union also zeroes its padding bytes, so the complete object representation is determined. See clause 6.2.6.1.

Zero initialization of a union initializes its first declared member to its zero value and normalizes the entire storage representation to zero bytes.

The second half of that sentence is what makes the rule usable. The first member may be smaller than the union, or may itself contain padding, and every byte of the union is zero regardless:

union Record
{
	struct
	{
		uint8_t tag;
		/* 3 bytes of padding on a target aligning uint32_t to 4 */
		uint32_t value;
	} pair;

	uint64_t bits;
};

union Record record;

record.pair.tag is 0, record.pair.value is 0, the padding between them is zero, and record.bits reads as 0 because all eight bytes are zero. Reading bits after zero initialization is defined under clause 6.5.2.3, and it produces a determined value because clause 6.2.6.1 zeroes padding.

Zero initialization of a pointer produces a null pointer, the same value a null constant produces. On supported targets the null representation is all-zero bytes, so zero-initialized pointer storage yields null pointers with no extra work. An implementation whose target uses a different null representation shall still produce a null pointer here, and shall document the cost. See clause 6.4.4.6 and behavior.md section 30.

Difference: ISO C99 zero-initializes objects with static storage duration and leaves automatic objects indeterminate. Ocean Edition I extends the rule C already had to every storage duration, and the syntax does not change. What disappears is the exception. See syntax.md section 28 and behavior.md section 42.

Cost.

The semantic guarantee does not require machine work that cannot be observed. Given:

int value;

value = calculate();

return value;

an implementation can prove that the initial zero is never read, and the initialization vanishes during optimization. The semantic model says the object begins valid. The generated code does only necessary work. See syntax.md section 29 and clause 4.7.1.

No uninit keyword.

Ocean Edition I does not provide a way to request an uninitialized object. If implementation evidence eventually shows a substantial need, the question can be revisited under clause 6.11. The default design is simpler without it, and a keyword whose meaning is "please give me a value nobody has defined" is exactly the kind of category this edition removes.

6.7.9.3 Explicit initialization of scalars and aggregates

An initializer for a scalar object is a single expression, optionally enclosed in braces, converted as if by assignment.

An initializer for an aggregate or a union is a brace-enclosed list.

The universal rule:

An explicitly supplied initializer sets the specified value. Every unspecified part begins as zero.

int values[8] = {
	[3] = 7,
};

produces:

0 0 0 7 0 0 0 0
struct Point point = {
	.x = 10,
};

initializes x to 10 and y to zero.

Element count deduction.

An array declarator with no element count and with an initializer takes the count from the initializer:

int values[] = { 1, 2, 3, 4 };     /* int [4] */
const char name[] = "ocean";       /* const char [6] */

The resulting type is a complete array type from that point onward.

Constraints.

The number of initializers shall not exceed the number of elements or members. An initializer for a member that does not exist is a constraint violation.

Every initializer for an object with static storage duration shall be a constant expression.

A designator shall be valid for the type being initialized: [N] for arrays and .member for structures and unions.

6.7.9.4 Designated initializers

Member and array designators are retained and are preferred in new code:

struct Point point = {
	.y = 20,
	.x = 10,
};

int values[8] = {
	[4] = 20,
};

Designators and positional initializers may be mixed. After a designated initializer, subsequent positional initializers continue from the position that follows the designated one.

Initializing the same element or member twice within one list is a constraint violation, because the reader cannot tell which value was intended. This is stricter than ISO C99, which lets the later initializer win silently.

Designated initializers are one of C99's best ergonomic improvements. A feature being newer than K&R does not make it suspicious. See syntax.md section 47.

6.7.9.5 String literal initializers

An array of character type may be initialized by a string literal:

char buffer[8] = "hello";      /* h e l l o \0 0 0 */
char exact[6] = "hello";       /* h e l l o \0 */

The characters of the literal, including its terminating null character, initialize the leading elements. Remaining elements are zero-initialized under clause 6.7.9.2.

A string literal with more characters than the array has elements is a constraint violation. The ISO C99 allowance for dropping the terminating null character is not retained, because an array that looks like a string and is not one is exactly the sort of quiet difference this edition removes.

An array of wchar_t may be initialized by a wide string literal in the same way.

ISO C99 mapping: 6.7.8.

6.7.9.7 const objects without an initializer

A definition of a const-qualified object without an initializer produces an object that is zero under clause 6.7.9.2 and that clause 6.7.3.1 forbids modifying. The object can therefore never hold any value other than zero.

const int limit;              /* zero, permanently */
const struct Point origin;    /* every member zero, permanently */

The construct is valid and it is almost always a mistake, so an implementation shall diagnose it. The diagnostic is a warning rather than an error, because the declaration has a determined meaning and a program may want a named zero.

An extern declaration of a const object is not affected, since it declares rather than defines:

extern const int limit;       /* fine: defined elsewhere, with an initializer */

A const object without an initializer is not usable as an integer constant expression, under clause 6.6.6, which requires an explicit initializer rather than merely a determined value.

ISO C99 mapping: new. In ISO C99 the object is indeterminate at block scope, which makes the declaration useless rather than merely suspicious.


6.7.10 Namespace Definitions

namespace-definition:
	namespace namespace-qualification { external-declaration-list-opt }

namespace-name:
	identifier

A namespace definition places the declarations it contains into the named namespace.

namespace net
{
	struct Header
	{
		int length;
	};

	int open(const char *host);

	static int retry_count;
}

Constraints.

A namespace definition shall appear at file scope or directly within another namespace definition. It shall not appear inside a block, inside a structure or union definition, or inside a parameter list.

A namespace-name shall not contain two consecutive underscores, under clause 6.4.2.0.

The body shall contain only external declarations, which is what file scope already permits.

main shall not be declared inside a namespace. See clause 6.9.6.7.

Semantics.

Each declaration in the body is a member of the namespace. A member with external linkage receives the linkage name clause 6.9.6.4 gives it, and a member with internal linkage or no linkage receives none, since it has no external name.

A namespace definition introduces no scope of its own beyond clause 6.2.1.3, no storage duration, and no initialization order. Removing the namespace line and its braces, and prefixing each declared name, produces a program with the same meaning.

Inclusion inside a namespace definition.

A #include directive is processed in translation phase 2, before the namespace definition is parsed. Text included inside the braces therefore becomes part of the namespace body, and every declaration it carries becomes a member.

namespace net
{
	#include <stdio.h>      /* every declaration becomes a member of net */
}

This is almost never intended, and an implementation should diagnose it. The #namespace directive is not affected, since clause 6.10.10 scopes it to its own source file. A program that wants a header's declarations inside a namespace should use a header that declares the namespace itself.

Nesting.

Namespace definitions nest, and the nested spelling and the qualified spelling are equivalent:

namespace net
{
	namespace tcp
	{
		int open(const char *host);
	}
}
namespace net::tcp
{
	int open(const char *host);
}

Both declare net::tcp::open.

Reopening.

A namespace may be defined more than once, in one translation unit or across several. Each definition adds members. There is no notion of a namespace being closed, and no declaration order requirement between definitions.

namespace net
{
	int open(const char *host);
}

namespace net
{
	int close(int handle);
}

Reopening does not redeclare anything. The ordinary rules of clause 6.9.5 govern whether two declarations of the same member are compatible.

Definitions of members.

A member declared in a namespace is defined by a declaration in the same namespace:

namespace net
{
	int open(const char *host)
	{
		...
	}
}

A member shall not be defined by a qualified declarator outside its namespace. The following is not accepted:

int net::open(const char *host)      /* invalid */
{
	...
}

Permitting it would add a second way to say one thing, which clause 6.7.2.1 declines to do for type spellings and this clause declines to do here.

static within a namespace.

static means what it means everywhere else: the entity has internal linkage and is not exported.

namespace net
{
	static int retry_count;

	static int backoff(int attempt)
	{
		return attempt * 2;
	}
}

retry_count and backoff are visible within the translation unit, are reachable as net::retry_count and net::backoff, and produce no symbol. They have no linkage name, under clause 6.2.2.3.

Members are exported by default, which is ordinary C behavior for a file-scope declaration without static.

Types, tags, and typedefs.

Every declaration in a namespace is a member of it, including tags, typedef names, and enumeration constants:

namespace net
{
	struct Header { int length; };
	typedef unsigned long index_t;

	enum State
	{
		STATE_IDLE,
		STATE_OPEN
	};
}

struct net::Header header;
net::index_t index;
enum net::State state = net::STATE_IDLE;

A tag is qualified after the struct, union, or enum keyword, because the qualification names the tag rather than the keyword.

Constraint. A qualified tag names an existing type and shall not define one. The defining forms take a bare identifier, so a type is defined where it belongs:

struct net::Header { int length; };   /* invalid: defines from outside */

This is the tag counterpart of the rule that a member shall not be defined by a qualified declarator outside its namespace.

Parsing. Interpreting net::index_t as a type specifier requires the same declaration information that interpreting index_t requires, and clause 6.7.7 already accepts that requirement. Qualification adds no new difficulty, since the qualification is resolved before the final identifier is classified.

Macros are not members of any namespace, since preprocessing precedes semantic analysis. See clause 6.10.11.

ISO C99 mapping: new. Added under clause 1.5.