C, Ocean Edition I

Annex G. Divergences from ISO C99

Annex
G
ISO C99 mapping
new
Status
Informative

G.0 How to Use This Annex

This is the complete list of differences between ISO C99 and Ocean Edition I. It is informative, and every entry points at the normative clause that governs.

Ocean Edition I derives from ISO C99, as clause 1.3 states. The differences fall into six kinds:

Removed. A construct ISO C99 has that this edition does not. In every case an existing C spelling already expresses the operation.

Defined. A behavior ISO C99 leaves undefined, unspecified, or implementation-defined that this edition gives one rule.

Canonicalized. A construct ISO C99 spells several ways that this edition spells one way.

Changed. A behavior that differs in result. This kind is rare and each instance is argued at its clause.

Adopted. A facility taken from a C standard published after 1999, or from settled practice that no standard has specified. Each adoption passes the test in clause 1.4.1.

Added. A facility no C standard provides. Exactly one exists, it passes the stricter test in clause 1.5.1, and clause G.6 records how.


G.1 Removed

Construct Replacement Clause
auto storage-class specifier ordinary block-scope declaration 6.4.1.1
register storage-class specifier ordinary declaration 6.4.1.1
Function declarations with empty parentheses f(void) 6.7.5.3
K&R function definitions prototype-style definitions 6.7.5.3
Tentative definitions one definition, plus extern declarations 6.9.2
extern declaration with an initializer a definition without extern 6.2.2.3
Bare inline and extern inline static inline 6.7.4
Implicit array-to-pointer conversion &array[0] 6.3.2.1
Implicit function-to-pointer conversion &function 6.3.2.1
Array-syntax function parameters T *p, or T (*p)[N] 6.7.5.2
static and qualifiers inside parameter array brackets write the pointer type 6.7.5.2
Variable-length arrays fixed arrays, or allocated storage 6.7.5.2
Multi-character character constants build the value explicitly 6.4.4.4
Trigraphs write the character 5.2.1.1
Digraphs write the punctuator 5.2.1.1
Sequence points defined evaluation order Annex E
Undefined behavior as a category defined result, trap, or unsafe memory 0.3
Complex arithmetic as a required facility optional extension 6.2.5.2
gets fgets 7.2.17
Integer constant 0 as a null pointer constant null or NULL 6.3.2.3
NULL as a library macro a keyword owned by the language 6.4.1.2

Note on what was not removed. goto, assignment expressions, the comma operator, compound literals, designated initializers, flexible array members, bitfields, unions, separate name spaces, C declarators, typedef, function pointers, and octal constants are all retained. The question was never whether a construct is old or unfashionable. The question is whether its rules are coherent. See syntax.md section 45.


G.2 Defined

G.2.1 Arithmetic

ISO C99 Ocean Edition I Clause
Signed overflow is undefined wraps, two's complement 6.5.6
Signed left shift overflow is undefined defined bit pattern 6.5.7
Out-of-range shift count is undefined defined trap 6.5.7
Signed right shift is implementation-defined arithmetic shift 6.5.7
Division by zero is undefined defined trap 6.5.5
INT_MIN / -1 is undefined defined trap 6.5.5
Narrowing to a signed type is implementation-defined truncates 6.3.1.3
Out-of-range float-to-integer conversion is undefined defined trap 6.3.1.4
Signed representation is one of three two's complement 6.2.6.2
Integer types may have padding bits none 6.2.6.2
Integer types may have trap representations none 6.2.6.2
Byte width is at least 8 exactly 8 6.2.6.1
Plain char signedness is implementation-defined never negative 6.2.6.3
Plain int bitfield signedness is implementation-defined signed 6.7.2.2

G.2.2 Evaluation

ISO C99 Ocean Edition I Clause
Subexpression order is unspecified left to right 6.5.0.1
Argument order is unspecified left to right 6.5.2.2
Multiple modification between sequence points is undefined defined by order 6.5.0.1
Loop termination may be assumed shall not be assumed 6.8.5.4
Optimizer may derive facts from undefined behavior shall not 4.7

G.2.3 Objects and storage

ISO C99 Ocean Edition I Clause
Automatic objects are indeterminate zero-initialized 6.7.9.2
Padding bytes are unspecified zeroed on zero initialization 6.2.6.1
Structure copy leaves padding unspecified deterministic 6.2.6.1
Effective type governs access no effective type 6.2.6.5
Type-based aliasing permits assumptions ordinary pointers may alias 6.5.0.4
Union type punning is not clearly permitted defined 6.5.2.3
Pointer comparison across objects is undefined compares addresses 6.5.8
Pointer arithmetic outside an array is undefined computes an address 6.5.6
setjmp locals are indeterminate without volatile last-stored value survives 7.2.12

G.2.4 Declarations and diagnostics

ISO C99 Ocean Edition I Clause
Enumeration compatible type is implementation-defined first type that fits, from a fixed list 6.7.2.3
Enumeration constants have type int have the enumerated type 6.4.4.3
An unrecognized escape sequence is undefined constraint violation 6.4.4.4
An unrepresentable integer constant is undefined constraint violation 6.4.4.1
Duplicate designated initializer silently wins constraint violation 6.7.9.4
External identifiers may be case-insensitive and 31 characters case-sensitive, at least 1024 Annex B
Falling off a value-returning function may be harmless diagnosed, or traps 6.8.6.4
Provable errors need not be diagnosed shall be diagnosed 4.3
Nothing is said about translation output artifacts shall be reproducible 4.10, Annex K
__DATE__ and __TIME__ read the clock the declared translation timestamp K.5.2
__FILE__ may carry the working directory recorded under a documented path rule K.6.3

G.2.5 Library

ISO C99 Ocean Edition I Clause
Overlapping memcpy is undefined overlap-safe 7.1.6.3
Zero-size copy with null pointers is undefined no effect 7.2.19
malloc(0) may return null or a unique pointer returns null 7.2.18
abs(INT_MIN) is undefined defined trap 7.2.18
Math functions may or may not set errno set errno 7.2.6
A negative char to <ctype.h> is undefined impossible 7.2.3
Handler-safe function set is left to folklore shall be published 7.2.14
time_t range is unconstrained at least 64 bits 7.2.23
Format string mismatch need not be diagnosed required diagnostic for literal formats 7.2.17

G.3 Canonicalized

ISO C99 accepts Ocean Edition I Clause
long int, signed int, unsigned long int, and similar one canonical spelling per type 6.7.2.1
Declaration specifiers in any order storage, qualifiers, type, declarator 6.7.8
memcpy and memmove as distinct contracts one behavior, both names 7.1.6.3
Several inline linkage categories static inline 6.7.4
_Bool and bool as two names for one type bool 6.7.2.1
Definition, tentative definition, extern declaration, extern definition definition, external declaration 6.9.2

G.4 Changed

These are the entries where a conforming ISO C99 program produces a different result under Ocean Edition I. There are five, and each is argued at its clause.

G.4.1 Mixed signed and unsigned comparison

-1 < 1u

is false in ISO C99 and true in Ocean Edition I. Comparison operators compare mathematical values. Clause 6.3.1.7.

Arithmetic operators are unchanged, so -1 + 1u still produces a large unsigned value, and clause 6.3.1.6 requires a diagnostic for it.

G.4.2 String literal type

sizeof("hello")

is unchanged at 6, but the type is const char [6] rather than char [6], so assigning a literal to a plain char * is now an error. Clause 6.4.5.

G.4.3 strncpy termination

strncpy always null-terminates and no longer pads the destination. The traditional behavior is available as strncpy_padded. Clause 7.2.19.

G.4.4 The integer constant zero in pointer position

int *p = 0;
if (p == 0)

are constraint violations. Write null or NULL. Clause 6.4.4.6.

A related consequence is that NULL can no longer be assigned to an integer, so int n = NULL; stops compiling. Both changes catch code that was relying on a literal zero meaning two different things.

G.4.5 Default initialization

An automatic object with no initializer reads as zero rather than as whatever was in the storage. A program that depended on the previous contents was already relying on undefined behavior, so this is a change in result only for programs that were already broken. Clause 6.7.9.2.


G.5 Adopted

Every entry here comes from a source cited in clause 2.4, and each passed the adoption test of clause 1.4.1. An adopted facility is respecified on this edition's terms, so the rule that governs is the Ocean Edition I clause rather than the standard of origin.

G.5.1 From ISO/IEC 9899:2024

Facility Ocean Edition I Clause
bool as a keyword the canonical spelling of the boolean type; _Bool is rejected 6.2.5.2, 6.4.1.2
true and false as keywords boolean constants of type bool 6.4.4.5
nullptr typed null constant adopted under the spellings null and NULL, which become keywords 6.4.4.6
nullptr_t adopted as null_t, with nullptr_t provided as an alias 6.2.5.2, 7.2.16
__has_include required, usable in #if and #elif 6.10.1
#warning required 6.10.5.1
Trigraphs removed the character means itself 5.2.1.1
Old-style function definitions removed prototype-style only 6.7.5.3
Two's complement required the only signed representation 6.2.6.2

Ocean Edition I goes further than C23 on the null constant as well. C23 adds nullptr while retaining NULL as a macro and retaining integer 0 as a null pointer constant, so it has three ways to write a null pointer. This edition has one constant with two spellings, and integer 0 is not among them.

Ocean Edition I goes further than C23 on the last three of the rows above. C23 removed trigraphs and K&R definitions, and this edition also removes the empty parameter list, which C23 retains. C23 requires two's complement and still permits padding bits and trap representations, which this edition does not.

G.5.2 From ISO/IEC 9899:2011

Facility Ocean Edition I Clause
max_align_t defined by <stddef.h> 7.2.16
Alignment query alignof through <stdalign.h>, required 7.2.21
gets removed use fgets 7.2.17
quick_exit permitted in a signal handler where provided 7.2.14
Anonymous members, atomics, threads, static assertions recommended extension spellings only 6.11.5

The last row is not an adoption. It records that where an implementation chooses to provide one of these, it should use the C11 spelling rather than invent one. Clause 1.4.2 explains the distinction.

G.5.3 From settled practice

Facility Ocean Edition I Clause
#pragma once required, with a specified failure direction 6.10.2.4
SOURCE_DATE_EPOCH recognized as a source of the declared translation timestamp K.5.1
Reproducible builds required, with verification tooling 4.10, Annex K
strtok_r required alongside strtok 7.2.19
Reentrant strerror, gmtime, localtime required, names documented 7.2.19, 7.2.23
64-bit time_t required 7.2.23
Format string checking required diagnostic for literal formats 7.2.17

G.5.4 What was considered and not adopted

These are the near misses, recorded so that the adoption test can be seen working rather than merely stated.

Facility Source Why not
constexpr C23 static const already serves, once clause 6.6.6 admits it as a constant expression. Fails test 3.
typeof C23 Adds a keyword to solve a macro problem, and this edition prefers static inline to macros. Fails tests 1 and 3.
Attribute syntax [[...]] C23 Large syntactic surface, and its uses are diagnostics rather than semantics. Fails tests 3 and 4.
Enumerations with a fixed underlying type C23 Clause 6.7.2.3 already fixes the underlying type deterministically, so the syntax adds nothing. Fails test 1.
Generic selection _Generic C11 Adds capability rather than removing a category, and belongs to the non-goals of syntax.md section 4. Fails test 1.
Atomics and threads C11 Large, and each requires a memory model this edition declines to define. Recommended extensions instead. Fails test 3.
_Alignas C11 An alignment query is needed and is required by clause 7.2.21. An alignment specifier is a new declaration form. Fails test 3.
Variably modified types C99, retained in C23 Removed by this edition, under clause 6.7.5.2.
Binary literals 0b1010 C23 Genuinely borderline. Adds no category and reads well, but adds lexical surface to solve a formatting preference. An implementation may provide it as an extension. Fails test 1 narrowly.
Digit separators C23 Same reasoning as binary literals.
u8, u, and U string prefixes C11 Source and execution character sets are both UTF-8 by default under clause 5.2.1, so ordinary literals already carry UTF-8. Fails test 1.

G.6 Added

One facility is in this category: namespaces, comprising the namespace definition form, the #namespace directive, the :: qualification operator, and the #import directive.

It is the only surface syntax this edition adds. syntax.md section 5.1 records the decision, and clause 1.5.1 states the test.

G.6.1 What it consists of

Construct Clause Purpose
namespace a { } 6.7.10 places a region of declarations into a namespace
namespace a::b { } 6.7.10 the same, nested
#namespace "a::b" 6.10.10 places the containing source file into a namespace
a::name 6.5.1.1 names a member of a namespace
::name 6.5.1.1 names a member of the global namespace
#import <h>, #import "h" 6.10.11 contributes a header's declarations rather than its text

G.6.2 How it passes the addition test

Test 1, it solves a problem C programmers already solve by hand. Every large C program invents a prefix convention. net_socket_open is a namespace written by hand, spelled by agreement, and invisible to the compiler.

Test 2, it produces the same result the manual practice produces. net::open has the linkage name net__open. A C translation unit declares int net__open(const char *); and calls it. No shim, no wrapper, no attribute. See clause 6.9.6.5.

Test 3, it introduces no new runtime concept. No object model, no lifetime, no storage duration, no control flow, no calling convention. Clause 6.9.6.3 lists what membership does not affect.

Test 4, it lowers to existing C constructs. Removing the namespace and renaming each member to its linkage name produces an equivalent C program.

#import lowers to #include with two mechanical adjustments: an include guard is added to any imported header that lacks one, which reproduces idempotency, and any macro the header relied on receiving from its own imports is obtained by the corresponding #include at the top of the header, which self-contained headers already satisfy under clause 6.10.11.2.

The one behavior that does not survive the rewrite is the macro isolation of clause 6.10.11.2. A lowered header sees the includer's macros again. This is a loss of a guarantee rather than a change of meaning for any header that does not inspect macros it never defined, which is what a self-contained header is. A header that does inspect them is not mechanically lowerable, and clause H.2 says so. Annex H clause H.2 gives the full rewrite.

Test 5, it leaves the ABI unchanged. Layout, alignment, parameter passing, and linkage are untouched. Only the spelling of an external name changes, and that spelling is an ordinary C identifier.

Test 6, it changes no existing program. Source declaring no namespace is entirely in the global namespace and behaves as it did.

Test 7, it conflicts with the white papers, so they were amended first. syntax.md section 5 listed import among vocabulary the edition should not need, and section 53 declined to invent import syntax. Section 5.1 now records the exception, and sections 4 and 53 point at it. Clause 6.11.3 released import from the reserved list and kept module and export reserved.

G.6.3 What was deliberately left out

The facility is a naming mechanism, and everything that would make it a module system was excluded.

Not provided Why
using, aliases, name injection qualification and outward lookup already reach every name
re-export a namespace does not acquire another's members
visibility control beyond static static already means not exported
interface files, module boundaries dependency remains the business of the header
defining a member from outside its namespace a second way to say one thing
overloading on namespace membership is not part of a type
extern "C" equivalent declaring at global scope already expresses it
implementation-defined mangling C interop requires a name a programmer can write

G.6.4 The cost

Two restrictions are the price of an injective, writable mangling.

Consecutive underscores are reserved. A namespace name, and an exported identifier inside a namespace, shall not contain __. Otherwise a::b__c and a__b::c collide. Clause 6.4.2.0.

A C library may collide. A C library exporting net__open collides with net::open. The mapping is injective within this edition and cannot police another language. An implementation diagnoses what it can see, and the residual risk is the one the prefix convention already carries. Clause 6.9.6.7.


G.7 Programs That Behave Identically

A program that satisfies all of the following behaves identically under ISO C99 and Ocean Edition I:

That description fits a large fraction of carefully written modern C, which is the point. Most of what this edition requires is what a careful C programmer already does. What changes is that the compiler now agrees.