C, Ocean Edition I

6.2 Concepts

Clause
6.2
ISO C99 mapping
6.2
Status
Normative

6.2.1 Scopes of Identifiers

An identifier can denote an object, a function, a tag or member of a structure, union, or enumeration, a typedef name, a label name, a macro name, or a macro parameter. The same identifier can denote different entities at different points in a program.

For each entity an identifier designates, the identifier is visible within a region of program text called its scope. Four scopes exist.

File scope

The identifier is declared outside any block and outside any parameter list. Its scope begins at the declarator and ends at the end of the translation unit.

Block scope

The identifier is declared inside a block or inside a parameter list of a function definition. Its scope begins at the declarator and ends at the closing brace of the block.

Function prototype scope

The identifier is declared inside a parameter list of a function declaration that is not a definition. Its scope ends at the closing parenthesis of the declarator.

Function scope

A label name has function scope. It is visible anywhere in the function that contains it, including before its own declaration.

6.2.1.1 Nested declarations

If an identifier declared in an inner scope denotes the same entity kind as an identifier visible from an outer scope, the inner declaration hides the outer one for the remainder of the inner scope.

An implementation should diagnose a shadowing declaration that is likely to be accidental. See annex-i-diagnostics.md clause I.3.

6.2.1.2 Point of declaration

An identifier's scope begins at the completion of its declarator and before any initializer. Therefore:

int value = 10;

void f(void)
{
	int value = value;   /* refers to the inner object, not the file-scope one */
}

The inner object is zero-initialized before its initializer runs, so this initializes it to zero rather than reading an indeterminate value. This is a consequence of clause 6.7.9.2 rather than an exception to it. An implementation shall diagnose a self-referential initializer of this shape, because the program almost certainly meant the outer object.

ISO C99 mapping: 6.2.1.

6.2.1.3 Namespace scope

A declaration contained by a namespace definition, or governed by a #namespace directive, is at namespace scope.

Namespace scope is not a fifth kind of scope. A declaration at namespace scope has file scope, with the visibility file scope gives it, and additionally has membership in a namespace. The two properties are independent, and the second one affects only how the name is written and what linkage name it receives.

An identifier at namespace scope is visible:

A namespace does not introduce a lifetime, a storage duration, or an initialization order. See clause 6.9.6.

ISO C99 mapping: new.


6.2.2 Linkages of Identifiers

An identifier declared in different scopes, or declared more than once in the same scope, can be made to refer to the same entity by linkage.

Three linkages exist.

External linkage

Each declaration of the identifier, in any translation unit of the whole program, denotes the same entity. Where namespaces are in use, identity is determined by the linkage name of clause 6.2.2.3 rather than by the identifier alone.

Internal linkage

Each declaration of the identifier within one translation unit denotes the same entity, and translation units do not share it. behavior.md calls this translation-unit-local linkage. Both names mean the same thing.

No linkage

Each declaration denotes a unique entity.

6.2.2.1 Determining linkage

6.2.2.2 Constraints

An identifier shall not be declared with internal linkage in one place and external linkage in another within the same translation unit.

A block-scope declaration of an object shall not use extern to redeclare an object that has a visible file-scope definition with internal linkage.

6.2.2.3 Linkage names

An entity with external linkage has a linkage name, which is the name by which the linker identifies it.

For an entity declared outside any namespace, the linkage name is the identifier itself. This is what ISO C99 does, and it is unchanged.

For an entity declared inside a namespace, the linkage name is formed by the rule in clause 6.9.6.4, which joins the enclosing namespace names and the identifier with a double underscore.

An entity with internal linkage or with no linkage has no linkage name, because it has no name outside its translation unit.

Two entities with external linkage are the same entity when their linkage names are the same. This is the rule that gives namespaces their effect: net::open and disk::open have the linkage names net__open and disk__open, so they are different entities, and a program may define both.

ISO C99 mapping: new. In ISO C99 the linkage name is always the identifier, so the concept needs no name.

6.2.2.4 What extern does not do

An extern declaration never defines an object. A declaration with extern and an initializer is a constraint violation:

extern int counter = 10;   /* invalid */

Write a definition, or write a declaration:

int counter = 10;          /* definition */
extern int counter;        /* declaration */

Difference: ISO C99 makes the first form a definition despite the extern. Ocean Edition I keeps one spelling for each concept. See syntax.md section 15 and behavior.md section 46.

ISO C99 mapping: 6.2.2.


6.2.3 Name Spaces of Identifiers

More than one entity may share an identifier when they occupy different name spaces. The name spaces are:

The last is added by clause 1.5 alongside the namespace mechanism. It exists so that qualification always works:

int net;                      /* an ordinary identifier */

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

void f(void)
{
	net = 1;                  /* the object */
	net::open(&host[0]);      /* the namespace */
}

A namespace name is looked up only where a namespace name is expected, which is before :: and after the namespace keyword. An ordinary identifier therefore never hides a namespace, and a namespace never hides an object or a function.

An implementation should nevertheless diagnose a namespace whose name is also an ordinary identifier in the same scope, since the reader has to work harder than the compiler does.

Therefore this remains valid:

struct Node
{
	int Node;
};

6.2.3.1 Name spaces and namespaces

The name spaces above and the namespaces of clause 6.9.6 are independent mechanisms that unfortunately share a word.

A name space in the sense of this clause is one of the categories that let struct Node and a variable Node coexist. There are five, and a program cannot create another.

A namespace is a named region a program declares. Each namespace has its own complete set of the name spaces above, so a tag in one namespace does not collide with a tag in another:

namespace net
{
	struct Header { int length; };
}

namespace disk
{
	struct Header { int magic; };
}

net::Header and disk::Header are unrelated types.

Where this specification needs to be unambiguous, it writes "name space" in two words for the C categories and "namespace" in one word for the declared region.

Design note. Separate name spaces are unusual, and they are coherent. Ocean Edition I removes categories that exist only because history accumulated them, not categories that carry meaning. Removing name spaces would break enormous quantities of ordinary C for a gain measured only in tidiness. See behavior.md section 97 and syntax.md section 59.7.

ISO C99 mapping: 6.2.3.


6.2.4 Storage Durations of Objects

An object has a storage duration that determines its lifetime. An object's lifetime is the portion of program execution during which storage is guaranteed to be reserved for it.

Every object begins its lifetime with a valid value. See clause 6.7.9.2.

An object exists, has a constant address, and retains its last-stored value throughout its lifetime. Referring to an object outside its lifetime is unsafe memory behavior.

6.2.4.1 Static storage duration

An object whose identifier is declared at file scope, or declared with static or extern in any scope, has static storage duration. Its lifetime is the entire execution of the program.

Its stored value is initialized once, before program startup, as required by clause 6.7.9.

6.2.4.2 Automatic storage duration

An object whose identifier is declared inside a block without static or extern, and every function parameter, has automatic storage duration.

Its lifetime begins on entry to the block or on entry to the function, and ends when execution of that block ends in any way. Entering the block recursively creates a new object each time.

A jump into a block from outside it does not begin a new lifetime for objects the jump skipped over. See clause 6.8.6.1.

Because there are no variable-length arrays, the size of an automatic object is known at translation time, and its storage requirement does not depend on control flow. See clause 6.7.5.2.

6.2.4.3 Allocated storage duration

Storage obtained from an allocation function has allocated storage duration. Its lifetime begins when the allocation function returns a pointer to it and ends when that pointer is passed to a deallocation function.

Allocated storage begins as raw storage with alignment suitable for any object type whose size does not exceed the requested size. Storing a valid representation of type T into suitably aligned allocated storage establishes a valid T object there. No further step and no hidden state are required. See clause 6.2.6.5 and behavior.md section 78.

Raw storage and the initialization rule.

Clause 6.7.9.2 requires every object to begin its lifetime with a valid value. Raw storage is not an object, so the two rules do not conflict, and the distinction is worth stating plainly because a reader will notice the tension.

A declaration creates an object, and clause 6.7.9.2 gives that object a value. An allocation function reserves storage and creates no object, so there is nothing yet for that rule to apply to. An object appears when a value is stored, and from that moment it has the value that was stored.

Reading raw storage.

Reading allocated storage through a character type before anything has been stored into it is defined, and yields values this specification does not predict. It is not unsafe memory behavior, and it does not trap.

This is a deliberate exception, and it is narrow. It applies only to storage obtained from an allocation function, only within the size that was requested, and only through a character type. It exists because a program that copies a buffer it has partly filled would otherwise be reading storage the specification refused to describe, and because an allocator cannot be required to zero storage it was asked only to reserve.

Reading raw storage through any other type is unsafe memory behavior, because the bytes are not known to form a valid representation of that type.

A program that wants allocated storage to begin as zero calls calloc, which clause 7.2.18 requires to zero it.

Note. The values obtained are unspecified rather than implementation-defined, and are listed in annex-c-implementation-defined-behavior.md clause C.9. An implementation may return recycled storage, and is not required to document what it contains.

6.2.4.4 Thread storage duration

Thread storage duration is not part of the core edition.

An implementation may provide it as a documented extension, in which case an object with thread storage duration has a lifetime equal to the lifetime of the thread that owns it, and is default-initialized on thread creation under clause 6.7.9.2.

ISO C99 mapping: 6.2.4.


6.2.5 Types

Types are partitioned as follows.

6.2.5.1 The void type

void denotes an empty set of values. It is an incomplete type that cannot be completed.

An expression of type void produces no value and shall not be used where a value is required.

6.2.5.2 Basic types

The Boolean type

bool is an unsigned integer type large enough to store the two values 0 and 1, which are spelled false and true. It is a fundamental type of the language rather than a library facility.

An object of type bool holds only 0 or 1. Storing any other scalar value into it converts that value under clause 6.3.1.2, so a bool never holds a value outside its two.

sizeof(bool) is 1 unless the target ABI requires otherwise, and the implementation shall document it.

Adopted from ISO/IEC 9899:2024. ISO C99 spells this type _Bool and provides bool as a macro in <stdbool.h>. Ocean Edition I makes bool the type and removes _Bool under clause 6.7.2.1. See clause 6.4.1.3.

Character types

Three character types exist: char, signed char, and unsigned char. They are three distinct types for the purpose of type compatibility.

char has the same size, alignment, and range of representable values as unsigned char, and its values are never negative. See clause 6.2.6.3.

Signed integer types

The standard signed integer types are signed char, short, int, long, and long long. Each has the canonical spelling given in clause 6.7.2.

Unsigned integer types

For each signed integer type there is a corresponding unsigned integer type occupying the same amount of storage and having the same alignment: unsigned char, unsigned short, unsigned int, unsigned long, and unsigned long long.

bool is an unsigned integer type for the purposes of conversion and promotion, with the additional property stated in clause 6.3.1.2.

Extended integer types

An implementation may provide extended integer types as a documented extension. An extended integer type participates in the integer conversion rank ordering of clause 6.3.1.1 at a rank the implementation documents.

Real floating types

float, double, and long double. Their representations are implementation-defined and shall be documented. Where an implementation claims IEC 60559 conformance, annex-f-floating-point.md applies.

The null type

null_t is the type of the null constant, spelled null or NULL, described in clause 6.4.4.6.

It has exactly one value, the null pointer value. It is not an integer type, not a pointer type, and not an arithmetic type. It is a scalar type.

An object of type null_t occupies the same storage and has the same alignment as void *, so sizeof(null_t) equals sizeof(void *).

The type name is declared by <stddef.h> under clause 7.2.16. The constants themselves are keywords and need no header.

#include <stddef.h>

static void clear(int **slot, null_t value)
{
	*slot = value;
}

Declaring an object of type null_t is permitted and rarely useful, since the object can hold no other value.

Adopted from ISO/IEC 9899:2024, where the corresponding type is nullptr_t and the constant is nullptr. See clause 1.4.

Complex types

Complex and imaginary types are not part of the core edition. An implementation may provide _Complex and its associated library as a documented extension. A program can detect its presence through the macro of clause 6.10.8.1. See clause 6.11.5.

Difference: ISO C99 requires complex arithmetic in a hosted implementation. Ocean Edition I makes it optional, because the facility is large, rarely used in the domain this edition serves, and independently specified elsewhere.

6.2.5.3 Derived types

From the basic types, these derived types are constructed:

6.2.5.4 Type categories

Note. An enumerated type is an integer type, and is therefore an arithmetic type and a scalar type. Wherever a clause requires an operand of arithmetic type, an operand of enumerated type satisfies the requirement, and the integer promotions of clause 6.3.1.1 apply to it. This is what clause 6.7.2.3 means when it says that ordinary integer conversions remain available to enumerated values.

A union type is not an aggregate type. Its members overlap rather than being laid out in sequence, and clauses that speak of aggregates do not apply to it unless they name unions as well.

6.2.5.5 Enumerated types

An enumerated type is a distinct integer type whose values include the values of its enumerators. Enumerators are values of the enumerated type rather than unrelated integer constants produced as a side effect of the declaration.

An object of enumerated type may hold any value representable in its compatible integer type, whether or not an enumerator names that value. Enumerators are names, not a closed runtime set. See clause 6.7.2.3 and behavior.md section 19.

6.2.5.6 Qualified types

Each unqualified type has qualified versions formed by applying const, volatile, restrict, or a combination. A qualified type has the same representation and alignment as its unqualified version.

ISO C99 mapping: 6.2.5.


6.2.6 Representations of Types

6.2.6.1 General

Every object has an object representation, which is the sequence of sizeof(T) bytes occupied by the object.

A byte is exactly eight bits. CHAR_BIT is 8 in every conforming implementation.

The object representation of any object may be inspected by accessing it through a pointer to a character type:

unsigned char *bytes = (unsigned char *)&object;

This access is defined for any valid object, including objects of structure and union type, and including padding bytes. See clause 6.5.0.4 and behavior.md section 76.

Structures and unions may contain padding bytes. Padding bytes are part of the object representation and do not correspond to any member. Two rules make padding predictable:

Therefore the representation of a valid initialized structure can be hashed, compared byte by byte, written to storage, and read back. See behavior.md section 21.

Applications should still avoid treating ABI-dependent layout as a portable external data format unless the layout was chosen for that purpose.

Difference: ISO C99 leaves padding-byte values unspecified and makes byte comparison of structures unreliable. Ocean Edition I defines them.

6.2.6.2 Integer types

For any integer type, every bit of the object representation is a value bit. There are no padding bits and there are no trap representations. Every bit pattern of an integer object represents a value of that type.

Signed integers use two's-complement representation. Sign-magnitude and ones-complement representations are not permitted.

For a signed integer type of width N:

minimum value = -2^(N-1)
maximum value =  2^(N-1) - 1

For an unsigned integer type of width N:

minimum value = 0
maximum value = 2^N - 1

The value bits of an unsigned integer type and of its corresponding signed integer type occupy the same positions, so a signed value that is nonnegative has the same representation as the corresponding unsigned value.

Byte ordering within a multibyte integer is determined by the target ABI and is implementation-defined.

Difference: ISO C99 permits three signed representations, permits padding bits, and permits trap representations. Ocean Edition I permits one representation and no padding. See behavior.md section 16.

6.2.6.3 The plain char type

char represents a byte-sized character unit whose values are never negative. Its range is 0 to 255.

A program requiring a signed byte-sized integer writes signed char. A program requiring an unsigned byte-sized integer may write either unsigned char or char, and should write unsigned char when the intent is arithmetic rather than text.

char remains a distinct type from unsigned char for type compatibility, so a char * and an unsigned char * are not interchangeable without a cast.

Difference: ISO C99 makes the signedness of plain char implementation-defined. Source whose correctness depended on that choice was already nonportable, and the migration is mechanical. See behavior.md section 15 and syntax.md section 34.

6.2.6.4 Floating types

The representation of a real floating type is implementation-defined and shall be documented.

An implementation should provide IEC 60559 binary formats where the target supports them, and should map float to binary32 and double to binary64. The format of long double is implementation-defined.

Floating arithmetic is never silently replaced by integer arithmetic. See annex-f-floating-point.md.

6.2.6.5 Object representation and object identity

Storing a valid object representation of type T into suitably aligned storage that is not currently within the lifetime of an object of another type establishes an object of type T at that location.

Copying the complete object representation of a valid object of type T into suitably aligned storage establishes an equivalent T there.

There is no effective-type state machine, and an implementation shall not infer from the type used in an earlier store that a later access through a different type is invalid. Aliasing rules are stated in clause 6.5.0.4. See behavior.md sections 78 and 79.

6.2.6.6 Alignment

Every complete object type has an alignment requirement, determined by the target ABI, which is a power of two.

An object of type T is suitably aligned when its address is a multiple of the alignment requirement of T.

Accessing an object through a pointer that is not suitably aligned for the accessed type either behaves as an ordinary access, on targets whose ABI documents that unaligned access is supported, or causes a defined trap. It is never a license for an unrelated transformation. See behavior.md section 74.

The alignment facilities of clause 7.2.21 are available where the implementation supports them.

ISO C99 mapping: 6.2.6.


6.2.7 Compatible Type and Composite Type

Two types are compatible when they are the same type, or when the rules of clause 6.7.2, clause 6.7.3, and clause 6.7.5 declare them compatible.

Because every function declaration is a prototype, there is no rule for reconciling a prototype with an unprototyped declaration. That entire category is gone. See clause 6.7.5.3 and syntax.md section 8.

The composite type of two compatible types is a type compatible with both, formed by taking the array element count where one type has it, and taking the parameter type list where one type has it. Since arrays always carry their count and functions always carry their parameter list, the composite type is usually just the type itself.

ISO C99 mapping: 6.2.7.