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 withf
orl
(C90 only). - These
<complex.h>
function names, or the same name suffixed withf
orl
(C99/C11/C17 only). - These
<complex.h>
function names, or the same name suffixed withf
orl
(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:
errno
math_errhandling
(C99)setjmp
va_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 __thread
is 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.
acos
asin
atan
atan2
ceil
cos
cosh
exp
fabs
floor
fmod
frexp
ldexp
log
log10
modf
pow
sin
sinh
sqrt
tan
tanh
cerf
cerfc
cexp2
cexpm1
clog10
clog1p
clog2
clgamma
ctgamma
cacospi
casinpi
catanpi
ccompoundn
ccospi
cerfc
cerf
cexp10m1
cexp10
cexp2m1
cexp2
cexpm1
clgamma
clog10p1
clog10
clog1p
clog2p1
clog2
clogp1
cpown
cpowr
crootn
crsqrt
csinpi
ctanpi
ctgamma
cr_acosh
cr_acospi
cr_acos
cr_asinh
cr_asinpi
cr_asin
cr_atan2pi
cr_atan2
cr_atanh
cr_atanpi
cr_atan
cr_compoundn
cr_cosh
cr_cospi
cr_cos
cr_exp10m1
cr_exp10
cr_exp2m1
cr_exp2
cr_expm1
cr_exp
cr_hypot
cr_log10p1
cr_log10
cr_log1p
cr_log2p1
cr_log2
cr_logp1
cr_log
cr_pown
cr_powr
cr_pow
cr_rootn
cr_rsqrt
cr_sinh
cr_sinpi
cr_sin
cr_tanh
cr_tanpi
cr_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
...
#endif
Valid:
#ifndef MY_HEADER_H
#define MY_HEADER_H
...
#endif
Stdlib 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;
}