1. Purpose
This document defines the behavioral model of C, Ocean Edition I.
The Ocean Edition I preserves the syntax, computational model, and programming character of C while normalizing historical irregularities into a smaller and more predictable collection of rules.
The guiding principle is:
Where traditional C admits multiple interpretations, historical exceptions, unspecified behavior, or context-dependent transformations, Ocean Edition I chooses the simplest behavior consistent with ordinary programmer intent and applies it uniformly.
This document describes that behavior component by component.
It is intentionally implementation-independent.
The language may be implemented by:
- a traditional native compiler,
- a small bootstrap compiler,
- a source translator,
- an interpreter,
- a JIT compiler,
- a cross compiler,
- or any other conforming implementation.
No particular compiler architecture is part of the language.
2. Fundamental Philosophy
Ocean Edition I does not attempt to make C a different kind of language.
It retains:
- explicit memory,
- pointers,
- arrays,
- structs,
- unions,
- enums,
- manual allocation,
- arithmetic close to machine arithmetic,
- function pointers,
- direct control flow,
- separate compilation,
- implementation-visible object representation,
- and the possibility of unsafe memory operations.
The language instead pursues:
One predictable rule for each construct.
Where traditional C provides several semantic categories for essentially the same operation, those categories should be collapsed wherever doing so does not destroy useful expressive power.
3. Program Structure
A program consists of one or more translation units.
Each translation unit contains declarations and definitions.
A translation unit may refer to entities defined in other translation units through external declarations.
A complete hosted program contains exactly one externally visible entry function:
int main(void)
or:
int main(int argc, char **argv)
Implementations may support additional environment-specific entry signatures.
Freestanding implementations need not provide main.
4. Source Encoding
Ocean Edition I source text is UTF-8.
Implementations shall accept the ordinary ASCII subset directly.
Ancient source-representation compatibility mechanisms are not part of the language.
In particular:
- trigraphs do not exist,
- alternate character-set encodings are not part of the core language,
- source characters mean the characters actually written.
Line endings are implementation-normalized.
5. Tokens
The language recognizes the familiar C token categories:
- identifiers,
- keywords,
- integer literals,
- floating literals,
- character literals,
- string literals,
- punctuation,
- operators,
- preprocessing directives where supported.
Whitespace separates tokens where necessary but otherwise has no semantic meaning.
Comments are treated as whitespace.
Both forms are supported:
/* comment */
and:
// comment
6. Identifiers
Identifiers use the familiar C form.
An identifier names an entity according to its scope and namespace.
Implementations shall support at least:
- ASCII letters,
- decimal digits after the first character,
- underscore.
Implementations may permit additional Unicode identifier characters.
Identifier comparison is case-sensitive.
Thus:
value
Value
VALUE
are three distinct identifiers.
7. Keywords
Ocean Edition I retains the established useful vocabulary of C.
Obsolete storage hints are not part of the language.
In particular:
auto
register
are removed.
The remaining core vocabulary includes familiar C constructs such as:
break
case
char
const
continue
default
do
double
else
enum
extern
float
for
goto
if
inline
int
long
restrict
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
while
and C99 facilities appropriate to the edition.
8. Preprocessing
Traditional preprocessing remains available where required for C source compatibility.
However, preprocessing is not considered part of the semantic type system.
Its result is source presented to the language proper.
The familiar directive forms may be supported:
#include
#define
#undef
#if
#ifdef
#ifndef
#elif
#else
#endif
#error
#line
#pragma
Ocean Edition I strongly prefers ordinary language facilities over macro abstraction when both can express the same concept.
For example:
static const int max_users = 64;
is preferred to:
#define MAX_USERS 64
and:
static inline int max(int a, int b)
{
return a > b ? a : b;
}
is preferred to a function-like macro.
This is a programming-model preference rather than a syntactic incompatibility requirement.
9. Includes
The familiar spelling remains:
#include <stddef.h>
#include "local.h"
An implementation may process includes textually, structurally, incrementally, or through another equivalent mechanism.
The observable declarations and definitions are what matter.
Repeated inclusion of the same logical interface should not require implementation-specific behavior beyond normal C compatibility.
10. Declarations
A declaration introduces the existence and type of an entity.
A definition additionally creates the entity or supplies its body.
Ocean Edition I minimizes declaration categories.
For objects there are fundamentally:
definition
external declaration
For functions there are fundamentally:
declaration
definition
Tentative definitions do not exist as a distinct semantic category.
11. Canonical Declaration Order
Declaration specifiers have one canonical conceptual order:
storage
qualifiers
type
declarator
For example:
static const unsigned long flags;
extern volatile unsigned char status;
Redundant permutations need not be accepted.
This reduces syntactic plurality without changing expressive power.
12. Canonical Primitive Type Spellings
The canonical primitive spellings are:
void
char
signed char
unsigned char
short
unsigned short
int
unsigned int
long
unsigned long
long long
unsigned long long
float
double
long double
_Bool
Redundant forms such as:
long int
signed int
unsigned long int
need not be accepted where they convey no additional information.
13. void
void represents the absence of a value.
A function returning no value uses:
void f(void)
{
}
An expression of type void does not produce a usable value.
void * remains the generic object-pointer type.
14. Boolean Values
C99's _Bool type is retained.
Its logical values are false and true.
Conversion from arithmetic or pointer values follows the familiar C truth rule:
zero false
null pointer false
anything else true
Conditions may directly consume scalar values:
if (count)
{
...
}
No separate Boolean-only condition syntax is introduced.
15. Character Types
Plain:
char
has one consistent signedness across all Ocean Edition I implementations.
It represents a non-negative byte-sized character unit.
Signed small-integer behavior is requested explicitly with:
signed char
Unsigned small-integer behavior is requested explicitly with:
unsigned char
This removes implementation-dependent behavior from plain char.
16. Integer Representation
Signed integers use two's-complement representation.
There are no:
- sign-magnitude integers,
- one's-complement integers,
- integer trap representations.
Every bit pattern of an ordinary integer type represents a value.
Integer widths remain properties of the selected target data model unless exact-width types are used.
17. Exact-Width Integer Types
Where supported by the standard environment, exact-width types include:
int8_t
uint8_t
int16_t
uint16_t
int32_t
uint32_t
int64_t
uint64_t
Address-sized integer types include:
intptr_t
uintptr_t
Code requiring exact layout should use explicit-width types rather than depending upon assumptions about int, long, or related types.
18. Floating-Point Types
The familiar types remain:
float
double
long double
Implementations shall document their representations.
On implementations claiming IEEE floating-point conformance, ordinary IEEE behavior applies consistently.
Floating arithmetic is not silently transformed into integer arithmetic.
NaN and infinity behavior follows the selected floating model.
19. Enumerations
An enumeration defines a meaningful integral type.
enum State
{
STATE_IDLE,
STATE_RUNNING,
STATE_STOPPED
};
The enumerators conceptually have type:
enum State
rather than merely being unrelated integer constants produced as a side effect of the declaration.
Ordinary integral conversions remain permitted where doing so preserves normal C ergonomics.
An enum value is not restricted at runtime to the set of named enumerators.
For example:
enum State state = (enum State)100;
has a valid enum representation if the underlying integer representation can hold that value.
Enumerators are names, not a closed runtime set.
20. Structures
A structure is an aggregate consisting of independently addressable members.
struct Point
{
int x;
int y;
};
Members appear in declaration order.
Implementations may insert padding to satisfy alignment requirements.
The layout for a given target is deterministic.
Structure assignment copies the complete value.
a = b;
produces a value-equivalent copy of b in a.
21. Structure Padding
Padding bytes are part of object representation but do not correspond to members.
Whenever an object is zero-initialized, its padding is also zeroed.
Whenever a structure value is copied, its resulting representation is deterministic.
Reading the raw byte representation of a valid initialized structure is defined.
This allows reliable:
- debugging,
- hashing of normalized representations,
- byte inspection,
- and reproducible serialization tooling.
Applications should still avoid treating ABI-dependent structure layout as a portable external data format unless that layout is intentional.
22. Unions
A union represents overlapping typed storage.
union Value
{
float f;
uint32_t u;
};
All members begin at the same storage location.
Writing one member and then reading another reinterprets the stored object representation through the requested member type.
For example:
union Value value;
value.f = 1.0f;
uint32_t bits = value.u;
is defined.
A union does not maintain an abstract hidden "active member" whose purpose is to prohibit representation inspection.
23. Bitfields
Bitfields retain normal C syntax:
struct Flags
{
unsigned int ready : 1;
unsigned int mode : 3;
};
Their physical layout is determined completely by the selected target ABI.
The implementation shall define:
- bit allocation direction,
- storage-unit size,
- alignment,
- boundary crossing behavior,
- signedness behavior,
- and padding.
There is no hidden implementation freedom once a target ABI has been selected.
24. Arrays
An array is a first-class fixed-size object consisting of contiguous elements of one type.
int values[16];
has type:
array of 16 int
The array remains an array in all expression contexts unless an explicit operation produces something else.
25. No Implicit Array Decay
Traditional array-to-pointer decay is removed.
Given:
int values[16];
the expression:
values
denotes the array.
If the address of its first element is needed:
&values[0]
is written explicitly.
Thus:
process(&values[0]);
passes an element pointer.
This follows the general rule:
An expression does not silently acquire a different type because of its surrounding context.
26. Array Parameters
Array-looking function parameters that are silently rewritten into pointers are not part of Ocean Edition I.
These forms are rejected:
void process(int values[]);
void process(int values[16]);
If the parameter is a pointer:
void process(int *values);
If it is a pointer to an array:
void process(int (*values)[16]);
The declaration directly represents the actual type.
27. Variable-Length Arrays
Variable-length arrays are not part of Ocean Edition I.
This is rejected when count is not a compile-time constant:
int values[count];
Fixed arrays remain ordinary C objects:
int values[64];
Runtime-sized storage is represented using explicit dynamic storage or another library mechanism.
28. Flexible Array Members
Flexible array members remain supported:
struct Packet
{
size_t length;
unsigned char payload[];
};
The flexible member contributes no fixed element count to the containing structure.
It refers to trailing storage associated with the complete allocated object.
29. Pointers
A pointer represents an address capable of designating an object or function of the appropriate category.
Normal syntax remains:
int *p;
&p;
*p;
p + n;
p - n;
p[i];
No ownership, borrow, fat-pointer, or reference model is introduced.
30. Null Pointers
NULL is the canonical source spelling for a null pointer value.
A null pointer designates no object or function.
Pointers created by default zero initialization begin as null pointers.
On supported Ocean Edition I targets, the null representation is normalized so that zero-initialized pointer storage yields a null pointer.
Code should prefer:
int *p = NULL;
to treating an arbitrary integer literal as pointer notation.
31. Pointer Equality
Two pointers compare equal when they represent the same address and pointer category.
A null pointer compares equal only to another null pointer of a compatible pointer category after ordinary conversions.
32. Pointer Ordering
Relational comparison of valid object pointers produces an address ordering within the implementation's address space.
Thus:
a < b
compares their addresses.
The operation is not limited only to pointers derived from the same array object.
This matches the ordinary systems-programming model of ordered machine addresses.
33. Pointer Arithmetic
Given a pointer to T:
T *p;
then:
p + n
advances by n objects of type T.
Likewise:
p - n
moves backward.
Subtraction of suitably related pointers produces an element distance.
Producing an address that cannot be represented by the target pointer model is invalid and causes defined failure rather than arbitrary undefined behavior.
Dereferencing an address that does not designate a valid object remains an unsafe memory error.
34. Pointer and Integer Conversion
uintptr_t and intptr_t, when provided, are capable of holding ordinary pointer address representations.
A round trip through uintptr_t preserves the address:
uintptr_t raw = (uintptr_t)p;
void *again = (void *)raw;
If the implementation's pointer model contains metadata that cannot survive such conversion, the implementation must expose that limitation rather than silently pretending ordinary integer round-trip semantics exist.
35. Functions
A function has:
- a return type,
- a complete parameter list,
- and optionally a variadic tail.
Every function declaration is a prototype.
This:
int f();
is not a valid unspecified-parameter declaration.
Zero parameters are written:
int f(void);
36. Function Definitions
Only prototype-style definitions exist.
int add(int a, int b)
{
return a + b;
}
K&R-style definitions do not exist.
37. Function Designators
A function identifier denotes the function itself.
It does not silently decay into a function pointer.
The address is obtained explicitly:
&compare
Thus:
int (*fn)(int, int) = &compare;
is the canonical pointer construction.
Both functions and function pointers may be called using normal call syntax.
38. Function Calls
Arguments are evaluated strictly from left to right.
Given:
f(a(), b(), c());
the order is:
a()
b()
c()
f(...)
This ordering is guaranteed.
There is no unspecified argument evaluation order.
39. Call Type Safety
A function call must agree with the function's declared type.
Calling a function through an incompatible function-pointer type is invalid.
If incompatibility is statically known, the implementation must diagnose it.
If incompatible invocation is encountered dynamically through unsafe casting, execution shall fail in a defined manner where detection is possible.
The language does not treat calling-convention mismatch as a legitimate optimization opportunity or meaningful program behavior.
40. Return Values
A non-void function returns a value compatible with its declared return type.
A void function returns no value.
Reaching the end of a non-void function other than main without returning a value is invalid.
Reaching the end of:
int main(void)
is equivalent to:
return 0;
41. Objects and Lifetimes
An object has:
- a type,
- storage,
- a lifetime,
- a value,
- and optionally linkage.
An object's lifetime begins when its storage becomes associated with that declared object or allocated object representation.
Every object begins its lifetime with a valid value.
42. Default Initialization
If no initializer is provided, an object is zero-initialized.
Thus:
int count;
begins as zero.
double value;
begins as positive zero.
int *pointer;
begins as a null pointer.
struct Point point;
begins with all members recursively zero-initialized.
Arrays are recursively zero-initialized.
Union default initialization initializes its first declared member to its zero value and normalizes the complete storage representation.
43. Explicit Initialization
An explicit initializer supplies the initial value.
int count = 10;
Aggregate initialization follows C99 rules.
Unspecified aggregate elements or members are zero-initialized.
Example:
int values[8] = {
[3] = 7,
};
produces:
0 0 0 7 0 0 0 0
44. Storage Duration
Ocean Edition I recognizes the familiar storage-duration categories:
- automatic,
- static,
- allocated,
- implementation-defined thread-local storage where extensions provide it.
Block-scope objects normally have automatic duration.
File-scope objects have static duration.
A block-scope static object has static duration.
45. static
static has two accepted contextual meanings derived from the same concept of locally owned persistent identity.
At file scope:
static int counter;
the entity has translation-unit-local linkage.
At block scope:
void f(void)
{
static int counter;
}
the object persists across function invocations.
Other historical uses are not retained.
In particular, array-parameter forms involving static are rejected.
46. extern
extern means:
This is a declaration of an entity defined elsewhere.
Thus:
extern int counter;
declares an object.
An extern declaration does not define or initialize storage.
Therefore:
extern int counter = 1;
is invalid.
47. File-Scope Definitions
At file scope:
int counter;
is a definition.
It defines one zero-initialized object.
There are no tentative definitions.
A second definition of the same externally linked entity is invalid unless a separate platform linkage mechanism explicitly provides otherwise.
48. Linkage
Entities may have:
- no linkage,
- translation-unit-local linkage,
- external linkage.
Ordinary file-scope definitions have external linkage unless declared static.
static file-scope entities have translation-unit-local linkage.
Block-local ordinary objects have no linkage.
extern declarations refer to externally linked entities.
48.1 Namespaces and Linkage
An entity with external linkage has a linkage name, which is the name the linker sees.
For an entity declared outside any namespace, the linkage name is the identifier itself. This is ordinary C, and it is unchanged.
For an entity declared inside a namespace, the linkage name is formed by joining the enclosing namespace names and the identifier with a double underscore.
Thus:
namespace net
{
int open(const char *host);
}
declares an entity whose qualified name is:
net::open
and whose linkage name is:
net__open
The mapping is fixed by the language rather than chosen by the implementation. A C translation unit therefore reaches the entity by declaring the linkage name:
int net__open(const char *host);
No shim is required in either direction, and the object file is an ordinary object file.
An entity with translation-unit-local linkage has no linkage name, because it has no external name. static inside a namespace means what static means everywhere else:
namespace net
{
static int retry_count;
}
retry_count is not exported. Its qualified name still exists for lookup within the translation unit, and no symbol appears in the object file.
Namespace membership is a property of the name, not of the entity's storage, lifetime, type, or representation. Two entities in different namespaces are as unrelated as two entities with different names, which is exactly what the prefix convention already achieved by hand.
49. const
const means that modification is not permitted through the qualified object or access path.
For an object declared directly const:
const int limit = 10;
the object may not be modified after initialization.
Removing the qualifier through a cast does not make mutation legal.
For:
int value = 10;
const int *p = &value;
the object itself remains mutable through other valid non-const access paths.
p simply cannot be used to modify it.
50. volatile
volatile means:
Every source-level access is externally observable and must occur.
Reads must actually read.
Writes must actually write.
The implementation may not remove, combine, invent, or reorder volatile accesses in a manner that changes their prescribed ordering relative to other volatile accesses.
volatile does not imply:
- atomicity,
- mutual exclusion,
- thread synchronization,
- or race freedom.
51. restrict
restrict expresses an explicit non-aliasing promise.
During the relevant execution scope, the restricted pointer and its derived access paths are the designated means by which the relevant storage is accessed according to the restriction contract.
Ordinary pointers do not imply non-aliasing.
Thus:
void copy(
float *restrict dst,
const float *restrict src,
size_t count
);
provides stronger alias information than:
void copy(
float *dst,
const float *src,
size_t count
);
52. inline
inline is an optimization intent, not a linkage subsystem.
The useful conventional form is:
static inline int min(int a, int b)
{
return a < b ? a : b;
}
Whether a call is physically inlined is always an implementation decision.
Bare inline and extern inline forms with historical C99 linkage semantics are not part of Ocean Edition I.
53. Arithmetic Expressions
Arithmetic operators retain their familiar syntax:
+
-
*
/
%
Arithmetic uses the language's usual integer and floating conversion rules, except where Ocean Edition I explicitly normalizes historically dangerous behavior.
54. Signed Integer Overflow
Signed integer overflow is defined.
Arithmetic uses finite-width two's-complement behavior.
For example, if int is 32 bits:
INT_MAX + 1
produces:
INT_MIN
The operation is not undefined.
Unsigned arithmetic retains modular behavior.
55. Integer Narrowing
Conversion from a wider integer type to a narrower integer type is deterministic.
The low-order destination-width bits are retained.
The resulting bit pattern is interpreted according to the destination type.
There is no implementation-defined signed narrowing behavior.
56. Integer Widening
Widening preserves the mathematical value where representable.
Signed values are sign-extended.
Unsigned values are zero-extended.
57. Signed and Unsigned Comparisons
Mixed signed and unsigned comparisons follow value-preserving semantics rather than blindly converting a negative signed operand into a large unsigned value.
For example:
-1 < 1u
is true.
The comparison is conceptually performed using the mathematical values of both operands where both can be represented in a suitable comparison domain.
Only when one side cannot be represented in such a common domain does the language fall back to width-aware magnitude rules.
This avoids one of C's most common silent arithmetic surprises.
58. Division
Integer division truncates toward zero.
For example:
7 / 3
produces:
2
and:
-7 / 3
produces:
-2
Division by zero causes a defined runtime trap.
The signed overflow case:
INT_MIN / -1
also traps because no representable signed result exists.
59. Remainder
Remainder follows division.
For valid operands:
a == (a / b) * b + (a % b)
Division by zero or remainder by zero traps.
60. Shifts
Shift operators retain:
<<
>>
The shift count must satisfy:
0 <= count < width of promoted left operand
An invalid count traps.
There is no undefined shift count behavior.
61. Left Shift
Left shift operates on the finite-width bit representation.
Bits shifted beyond the width are discarded.
Signed left shift does not become undefined merely because the mathematical result exceeds the positive signed range.
The resulting bit pattern is interpreted according to the left operand's resulting integer type.
62. Right Shift
Unsigned right shift inserts zero bits.
Signed right shift is arithmetic.
The sign bit is replicated.
Therefore behavior is consistent across implementations.
63. Unary Negation
Unary:
-x
uses the same finite-width arithmetic rules as subtraction from zero.
Negating the minimum representable signed value therefore wraps according to two's-complement representation rather than invoking undefined behavior.
64. Increment and Decrement
The familiar operators remain:
++x
x++
--x
x--
Their value semantics remain those of C.
Evaluation order is governed by the universal left-to-right sequencing rules.
Overflow follows the same deterministic integer rules as ordinary addition and subtraction.
65. Evaluation Order
Unless syntax explicitly requires otherwise, subexpressions evaluate left-to-right.
This applies to:
- binary arithmetic operands,
- function arguments,
- comparison operands,
- assignment subexpressions,
- comma-separated expressions where the comma operator is present,
- initializer expressions.
Operator precedence determines grouping.
Evaluation order determines execution.
They are separate rules.
66. Short-Circuit Operators
Logical AND:
a && b
evaluates a first.
b is evaluated only if a is true.
Logical OR:
a || b
evaluates a first.
b is evaluated only if a is false.
The result is logically false or true.
67. Conditional Operator
The conditional operator:
condition ? a : b
evaluates condition first.
Exactly one of a or b is evaluated.
68. Assignment
Assignment retains:
lhs = rhs
The right-hand expression is evaluated and converted to the destination type.
The resulting value is stored in the destination object.
Assignment itself produces the stored value as an expression result, preserving traditional C behavior.
69. Compound Assignment
Operators such as:
+=
-=
*=
/=
%=
<<=
>>=
&=
|=
^=
perform the corresponding operation and assignment.
The left operand is evaluated once.
Overflow and invalid arithmetic follow the same rules as the corresponding ordinary operator.
70. Comparison
The familiar comparison operators remain:
==
!=
<
<=
>
>=
Arithmetic values compare according to their normalized conversion rules.
Pointers compare according to pointer equality and ordering semantics.
The result is logically false or true.
71. Bitwise Operators
Bitwise operators remain:
&
|
^
~
They operate directly on integer bit representations.
Signed integer representation is two's-complement, so bitwise behavior is deterministic.
72. Logical Truth
Scalar values have truth according to:
integer zero false
floating zero false
null pointer false
all other scalar values true
NaN is true because it is not numerical zero.
73. sizeof
sizeof returns the storage size of a type or object in units of char.
It does not evaluate an ordinary value operand.
Because variable-length arrays are absent, sizeof is not made dynamically evaluative by array extents.
For:
int values[16];
sizeof(values)
is the size of the entire array.
Arrays do not decay.
74. Alignment
Objects have alignment requirements defined by their types and target ABI.
An implementation shall provide the alignment facilities required by its supported C environment.
Misaligned accesses may either be supported according to the target or cause defined failure.
They are not treated as a license for unrelated program transformations.
75. Casts
C-style casts remain:
(type)value
Casts may explicitly request conversions between compatible categories.
Unsafe casts remain possible.
For example:
void *p;
int *q = (int *)p;
The programmer remains responsible for ensuring that dereferencing the resulting pointer is valid.
Casts do not suppress the fundamental semantics of object mutability, valid storage, or call compatibility.
76. Object Representation
Every object has a byte representation.
Its representation may be inspected through character-type access.
For example:
unsigned char *bytes = (unsigned char *)&object;
is valid.
Primitive integer bit patterns are fully defined according to the target representation rules.
Structures may contain deterministic padding.
Unions intentionally expose overlapping representations.
77. Aliasing
Ordinary object pointers may alias.
Type difference alone does not allow an implementation to assume that two addresses refer to distinct storage.
Explicit restrict contracts or provable program facts may establish stronger non-aliasing guarantees.
Character pointers may inspect the representation of any object.
78. Allocated Storage
Dynamically allocated storage begins as raw storage with suitable alignment according to the allocator's contract.
The storage becomes associated with an object representation when a value of that type is stored into it or copied into it.
The rule is intentionally direct:
Storing a valid
Trepresentation into suitably aligned storage establishes a validTobject there.
No additional hidden effective-type state machine is required.
79. Byte Copying
Byte copying preserves object representation.
Copying the complete representation of a valid object of type T into suitably aligned destination storage establishes an equivalent T representation there.
This makes ordinary low-level copying behavior explicit.
80. memcpy and Overlap
Ocean Edition I standard-library behavior should prefer overlap-safe copying.
A conforming memcpy may provide the same overlap-safe semantics traditionally associated with memmove.
Thus copying bytes between overlapping regions produces the result obtained as though the source bytes were first preserved and then written to the destination.
This removes an unnecessary distinction where implementations can provide the intuitive behavior efficiently.
memmove may remain for source compatibility.
81. String Literals
A string literal is an immutable character array.
For:
"hello"
the conceptual type is:
array of 6 const char
including the terminating zero byte.
Writing to string literal storage is invalid.
The type system reflects the actual mutability.
82. Adjacent String Literals
Adjacent string literals concatenate at translation time:
const char *message =
"hello "
"world";
This remains supported.
83. Character Literals
A character literal contains one character value:
'a'
Multi-character character constants such as:
'abcd'
are not part of Ocean Edition I.
They are too implementation-dependent to justify preserving.
84. String and Memory Library Behavior
Standard string and memory functions should have deterministic preconditions and failure behavior.
Operations which can naturally support overlap safely should do so.
Operations requiring valid null-terminated strings still require them.
Passing an invalid pointer remains an unsafe memory error.
Library semantics should avoid introducing arbitrary undefined behavior where a deterministic result or trap can reasonably be specified.
85. Blocks
A block:
{
...
}
introduces a nested scope.
Automatic objects declared within the block normally live until the block exits.
static block objects persist beyond individual executions.
86. if
Conditional execution remains:
if (condition)
{
...
}
else
{
...
}
The condition is evaluated once.
Nonzero scalar values are true.
Zero scalar values are false.
87. while
A while loop:
while (condition)
{
body();
}
evaluates the condition before each iteration.
88. do while
A do loop:
do
{
body();
}
while (condition);
executes the body once before testing the condition.
89. for
A for loop retains normal C form:
for (init; condition; step)
{
body();
}
Its conceptual execution is:
init
while (condition)
{
body
step
}
with ordinary lexical scoping.
90. break
break exits the innermost enclosing loop or switch.
91. continue
continue advances to the next iteration of the innermost enclosing loop according to that loop's defined control path.
92. switch
switch retains standard C behavior.
switch (value)
{
case 1:
...
break;
default:
...
break;
}
The controlling expression is evaluated once.
Case labels are compile-time integral constants compatible with the controlling type.
93. Fallthrough
Case fallthrough remains legal.
case 1:
prepare();
case 2:
execute();
break;
The implementation should diagnose suspicious accidental fallthrough by default.
The language does not introduce mandatory new syntax merely to preserve an existing useful behavior.
94. goto
goto remains a primitive control-flow operation.
goto cleanup;
jumps to the matching label within the function, subject to object-lifetime restrictions.
It remains useful for low-level cleanup and state-machine code.
95. Labels
Labels have function scope.
They identify valid jump destinations.
Jumping into a region in a manner that would violate the initialization or lifetime requirements of objects declared there is invalid.
96. Scope
The familiar scope categories remain:
- file scope,
- function scope,
- block scope,
- function-prototype scope where relevant.
Names resolve according to the nearest applicable declaration in the appropriate namespace.
97. Namespaces
C's separate identifier namespaces are retained where they provide useful compatibility:
- ordinary identifiers,
- structure/union/enum tags,
- labels,
- member names.
Thus:
struct Node
{
int Node;
};
may remain legal because the names occupy distinct namespaces.
This is unusual but coherent and deeply embedded in C syntax.
98. typedef
A typedef introduces an alias.
typedef unsigned long index_t;
It does not create a new nominal type.
Aliases are interchangeable with their underlying type.
99. Initializer Lists
Aggregate initializer lists retain C99 syntax.
struct Point point = {
10,
20,
};
Designated initializers remain available:
struct Point point = {
.y = 20,
.x = 10,
};
Missing members are zero-initialized.
100. Compound Literals
Compound literals remain:
(struct Point){
.x = 10,
.y = 20,
}
They create an object value of the indicated type with the appropriate storage duration for their context.
101. Designated Initializers
Both member and array designators remain:
struct Point point = {
.x = 10,
};
and:
int values[8] = {
[4] = 20,
};
Unspecified portions are zero-initialized.
102. Variadic Functions
Variadic functions remain:
int printf(const char *format, ...);
Named arguments are evaluated left-to-right before variadic arguments continue in the same order.
Variadic arguments undergo the edition's defined default argument conversions.
The language should specify these conversions explicitly and consistently.
103. Default Argument Promotions
Where compatibility with C variadic conventions requires promotion, the rule remains explicit.
For variadic arguments:
- narrower integer types are promoted according to the defined integer promotion model,
floatpromotes todouble.
These promotions are part of the variadic calling contract rather than a general excuse for hidden type changes elsewhere.
104. Sequence and Side Effects
The traditional concept of sequence points is replaced by direct evaluation-order semantics.
Each expression has a deterministically ordered sequence of evaluations.
A modification becomes visible to evaluations sequenced after it.
Programs need not reason about "unsequenced modifications" as a special semantic category.
105. Undefined Behavior
Ocean Edition I intentionally minimizes undefined behavior.
The language distinguishes:
Defined behavior
The operation has a specified result.
Defined trap
The operation is invalid and execution terminates through the implementation's defined trap mechanism.
Unsafe memory behavior
The program accesses storage that does not designate a valid object in the required manner.
Unsafe memory behavior remains possible because Ocean Edition I remains a manually managed systems language.
Undefined behavior is not used merely to simplify optimizer reasoning where a deterministic machine-oriented behavior is available.
106. Typical Defined Traps
Examples include:
integer division by zero
integer remainder by zero
invalid shift count
unrepresentable signed division result
detected incompatible function invocation
certain impossible pointer-address operations
The implementation may diagnose these statically when provable.
107. Unsafe Memory Errors
Examples include:
dereferencing an invalid pointer
use-after-free
writing outside an allocated object
reading outside an allocated object
double free
invalid allocator ownership
accessing storage after lifetime end
Ocean Edition I does not impose mandatory runtime memory safety.
Implementations may provide diagnostic, sanitizer, or checked modes.
108. Compile-Time Diagnostics
A conforming implementation should reject or diagnose errors knowable at translation time.
Examples include:
int a[4];
a[7] = 1;
when the out-of-bounds access is statically evident.
Likewise:
- incompatible function calls,
- duplicate definitions,
- invalid declarations,
- invalid constant shifts,
- invalid constant division,
- writes to known const objects,
- impossible initializer conversions.
A deterministic language should use knowledge it already possesses.
109. Warnings
Non-invalid but suspicious constructs should be diagnosed where practical.
Examples include:
- implicit case fallthrough,
- ignored return values where relevant,
- suspicious narrowing,
- comparison of unrelated enum categories,
- pointless casts,
- unreachable code,
- shadowing likely to be accidental,
- unused variables,
- unused internal functions.
Warnings do not redefine valid program behavior unless a strict conformance profile explicitly promotes them to errors.
110. Hosted and Freestanding Environments
Ocean Edition I supports both traditional C execution models.
A hosted implementation provides the expected language environment and standard library profile.
A freestanding implementation may omit facilities requiring an operating system or hosted runtime.
The core language semantics remain the same.
Pointers, arithmetic, objects, arrays, structs, unions, control flow, and initialization do not change merely because the program is freestanding.
111. Standard Library Philosophy
The standard library should follow the same principles as the language.
Where traditional behavior is already sensible, preserve it.
Where several APIs differ only because of historical constraints, normalize behavior where compatibility permits.
Where an operation can fail, define the failure contract.
Where overlap can be handled safely without changing the programmer's conceptual operation, prefer safe overlap semantics.
Where object sizes are available, implementations should expose diagnostics and checked variants without changing ordinary C syntax.
112. Implementation-Defined Behavior
Some implementation-defined behavior remains unavoidable.
Examples may include:
- sizes of
short,int,long, and pointers, - alignment requirements,
- structure layout according to ABI,
- endianness,
- representation of
long double, - available address width,
- calling conventions,
- environment-specific library behavior.
However:
Implementation-defined behavior must correspond to a genuine target property, not merely to historical specification indecision.
If every supported modern target can share one reasonable rule, the language should prefer defining it universally.
113. Unspecified Behavior
Ocean Edition I should contain very little unspecified behavior.
Where multiple execution orders are observable, an order should ordinarily be chosen.
Where multiple representations are possible only because targets genuinely differ, the property should be implementation-defined.
"One of several things happens and the implementation need not say which" should be treated as a last resort.
114. Compatibility Principle
Ocean Edition I favors source that is already valid, ordinary-looking C.
When behavior differs from traditional C, conversion should usually be mechanical.
Examples include:
implicit array decay
→ write &array[0]
function address decay
→ write &function
old f()
→ write f(void)
extern definition
→ remove extern
register object
→ remove register
VLA
→ use explicit dynamic storage
multi-character literal
→ construct the intended value explicitly
The language should remain close enough to traditional C that moving code between them is an engineering task rather than a port to a different programming model.
115. Behavioral Summary
The Ocean Edition I can be summarized through a small number of universal laws.
Law 1 — Syntax means what it says
An array declaration creates an array.
A pointer declaration creates a pointer.
An external declaration declares something external.
A definition defines something.
Law 2 — Expressions keep their types
Arrays do not become pointers merely because context wants one.
Functions do not become function pointers merely because context wants one.
Explicit syntax performs explicit transformations.
Law 3 — Evaluation has an order
Ordinary subexpressions evaluate left-to-right.
Programs do not depend upon compiler-chosen argument order.
Law 4 — Objects begin valid
Every object begins its lifetime with a defined value.
No ordinary object begins as semantic poison.
Law 5 — Arithmetic describes finite machines
Signed integers use two's-complement behavior.
Overflow is defined.
Shifts are defined.
Invalid arithmetic traps.
Law 6 — Target differences must be real
ABI, alignment, widths, and endianness may differ.
Arbitrary historical ambiguity should not.
Law 7 — Optimization does not define semantics
A program's meaning is determined by the language.
Implementations optimize that meaning.
The language does not declare inconvenient executions nonexistent merely to aid optimization.
Law 8 — Dangerous is allowed
Pointers remain unsafe.
Manual storage remains unsafe.
Casts remain powerful.
Direct hardware interaction remains possible.
Ocean Edition I does not confuse danger with ambiguity.
Law 9 — Existing C syntax is preferred
New syntax is not introduced merely to repair a rule that existing C syntax can already express clearly.
Law 10 — Fewer categories are better
Where two language concepts exist only because history accumulated them separately, Ocean Edition I seeks one general rule.
116. Final Behavioral Thesis
The behavioral contract of C, Ocean Edition I is intentionally simple:
Programs should behave the way an experienced systems programmer expects the machine-oriented C source to behave, unless there is a compelling technical reason otherwise.
When the programmer writes:
int x;
x has a value.
When the programmer writes:
a() + b()
a() happens before b().
When the programmer writes:
int values[16];
values remains an array.
When the programmer writes:
&values[0]
they have explicitly requested an address.
When signed arithmetic exceeds its range, finite-width arithmetic still occurred.
When bits are shifted right through a signed value, the operation has one meaning.
When a union member is inspected through another member, the programmer is intentionally examining overlapping representation.
When const is written, mutation is forbidden.
When extern is written, the declaration does not secretly become a definition.
When restrict is written, it communicates an actual aliasing guarantee.
When volatile is written, accesses actually happen.
When the target genuinely differs, the implementation documents the difference.
And when the language can reasonably provide one answer, it does.
Ocean Edition I therefore does not attempt to protect the programmer from the machine.
It attempts to protect the programmer from C's historical uncertainty about the machine.
That is the behavioral foundation of C, Ocean Edition I.