This translates sections 7.1.3 ("Reserved Identifiers") and 7.13 ("Future library directions") of the ISO C90 specification into English (and regexes).
This document will also cover the additions introduced by C99's section 7.26 ("Future library directions"), C11's section 7.31 ("Future library directions"), and C17's section 7.1.3 ("Reserved Identifiers").
We attempt to cover C2x's additions as well. Note that C2x is still unstable and subject to change.
The ISO C specification describes five different scopes:
- File scope
- Function scope
- Block scope
- Function prototype scope
- Struct, union, and enum scope
The specification also describes four different namespaces:
- Label namespace: labels
- Tag namespace: structs, unions, and enums
- Member namespace: the members of structs or unions
- Ordinary namespace: typedefs, variables, functions, enum constants
Since the label and member namespace are always function and struct/union scoped respectively, we can ignore them for the purposes of this document.
Identifiers collide unacceptably when they have both the same namespace and the same scope. Otherwise, the collision is acceptable. This feature is commonly known as "shadowing".
The rules below may specify a namespace and a scope. Note that macros potentially apply to any namespace/scope as they simply find & replace identifiers.
All rules apply to C90 unless stated otherwise.
-
Reserved identifiers (any scope) & macro names:
/^_[A-Z]\w*$/(exemptions for C17)/^__\w*$/(exemptions for C17)/^E[0-9A-Z]\w*$//^LC_[A-Z]\w*$//^SIG_?[A-Z]\w*$//^(PRI|SCN)[a-zX]\w*$/(C99)/^U?INT\w*(_MAX|_MIN|_C)$/(C99)/^FE_[A-Z]\w*$/(C11)/^ATOMIC_[A-Z]\w*$/(C11)/^TIME_[A-Z]\w*$/(C11)/^U?INT\w*_WIDTH$/(C2x)/^(FP|MATH)_[A-Z]\w*$/(C2x)/^(DBL|DEC32|DEC64|DEC128|DEC|FLT|LDBL)_[A-Z]\w*$/(C2x)- Any stdlib macro name (only if included).
-
Reserved ordinary identifiers (file scope):
/^_\w*$//^(is|to)[a-z]\w*$//^(str|mem|wcs)[a-z]\w*$//^u?int\w*_t$/(C99)/^(atomic|memory)_[a-z]\w*$/(C11)/^memory_order_[a-z]\w*$/(C11)/^(cnd|mtx|thrd|tss)_\w*$/(C11)- Any stdlib identifier in the ordinary namespace (only if included).
- Any
<math.h>function name suffixed withforl(C90 only). - These
<complex.h>function names, or the same name suffixed withforl(C99/C11/C17 only). - These
<complex.h>function names, or the same name suffixed withforl(C2x). - These
<math.h>function names, or the same name suffixed withf,l,d32,d64, ord128(C2x).
-
Reserved tag identifiers (file scope):
/^_\w*$/- Any stdlib identifier in the tag namespace (only if included).
-
Reserved ordinary identifiers with external linkage:
- Any stdlib identifier (or future-reserved identifier) with external
linkage. This includes:
errnomath_errhandling(C99)setjmpva_copy(C11)va_end
- Any stdlib identifier (or future-reserved identifier) with external
linkage. This includes:
-
C17 exemptions
- C17 modified some wording to allow
^_[A-Z_]macros to be defined by the programmer when they match an existing keyword. For example:#define _Thread_local __threadis a-okay.
- C17 modified some wording to allow
-
C2x redefinition
- C2x changed some wording to redefine future identifiers as "potentially reserved identifiers". The above identifier patterns which do not currently coincide with existing identifiers provided by the standard library no longer trigger undefined behavior. In other words, after C2x is adopted, this document will no longer be necessary.
acosasinatanatan2ceilcoscoshexpfabsfloorfmodfrexpldexploglog10modfpowsinsinhsqrttantanh
cerfcerfccexp2cexpm1clog10clog1pclog2clgammactgamma
cacospicasinpicatanpiccompoundnccospicerfccerfcexp10m1cexp10cexp2m1cexp2cexpm1clgammaclog10p1clog10clog1pclog2p1clog2clogp1cpowncpowrcrootncrsqrtcsinpictanpictgamma
cr_acoshcr_acospicr_acoscr_asinhcr_asinpicr_asincr_atan2picr_atan2cr_atanhcr_atanpicr_atancr_compoundncr_coshcr_cospicr_coscr_exp10m1cr_exp10cr_exp2m1cr_exp2cr_expm1cr_expcr_hypotcr_log10p1cr_log10cr_log1pcr_log2p1cr_log2cr_logp1cr_logcr_powncr_powrcr_powcr_rootncr_rsqrtcr_sinhcr_sinpicr_sincr_tanhcr_tanpicr_tan
This violates the reserved identifier rules:
static int _foo = 1;
int main(void) {
return _foo;
}This does not:
int main(void) {
static int _foo = 1;
return _foo;
}It's easy to run into file-scoped identifiers that begin with to, even if you
do not mean it as a to[something] conversion function.
Invalid:
static int today = 1;
int main(void) {
return today;
}Valid:
int main(void) {
static int today = 1;
return today;
}Likewise, many words begin with str.
Invalid:
static int stream = 1;
int main(void) {
return stream;
}Valid:
int main(void) {
static int stream = 1;
return stream;
}Basically any macro that starts with E is a no-no, even if you haven't
included <errno.h>.
Invalid:
#define ELEMENT 1
int main(void) {
return ELEMENT;
}It seems like it would be easy to avoid collisions with the <signal.h> rule,
but there are a number of words that start with "sig". "Sigma" and "signature"
are two common ones you might find in programming.
Invalid:
#define SIGMA 1
int main(void) {
return SIGMA;
}Double-underscore prefixes are invalid everywhere.
Invalid:
int main(void) {
int __foo = 1;
return __foo;
}An underscore followed by an uppercase letter is invalid everywhere.
Invalid:
int main(void) {
int _Foo = 1;
return _Foo;
}This very commonly used pattern in C programming violates the reserved rules
as invalid as _[A-Z] prefixes are invalid everywhere.
Invalid:
#ifndef _MY_HEADER_H
#define _MY_HEADER_H
...
#endifValid:
#ifndef MY_HEADER_H
#define MY_HEADER_H
...
#endifStdlib identifiers are only invalid if included and used in the same namespace (the tag namespace in this case):
Invalid:
#include <time.h>
union tm {
int foo;
char bar;
};Valid:
#include <time.h>
static int tm(int x, int y) {
return x + y;
}