January 9th, 2023

On leading underscores and names reserved by the C and C++ languages

The C and C++ languages reserve certain categories of names for the implementation, which means that you cannot use them in your own code. Some are reserved unconditionally, precluding their use for for variable names, parameter names, classes, methods, macros, whatever. Others are reserved only in certain contexts.

The rules for C++ are collected in the [lex.name] chapter. The rules for C happen to match the C++ rules for the most part, (section 7.1.3 “Reserved identifiers“), so that makes things easier to remember.

Pattern Conditions
Begins with two underscores Reserved
Begins with underscore and uppercase letter Reserved
Begins with underscore and something else Reserved in global scope (includes macros)
Contains two consecutive underscores Reserved in C++ (but okay in C)

Note that a popular convention of prefixing private members with an underscore runs afoul of these rules if the member name begins with an uppercase letter.

class Widget
{
public:
    Widget();

private:
    int _size; // okay
    void _Toggle(); // not okay
};

The C language does not have namespaces, so it also must reserve names in the global namespace for future expansion. Some names may not be used by symbols with external linkage. You can use them for type names, enumeration members, local variables, and functions or global variables declared with static storage class, but not for extern functions or extern global variables.

// Not allowed: Identifier with external linkage
// beginning with "str" and a lowercase letter.
int strategy;
void strafe() { /* ... */ }

// Allowed: Identifier with internal linkage beginning
// with "str" and a lowercase letter.
static int strawberry;
static void stream_video() { /* ... */ }

Furthermore, if you include the corresponding header file, the names are permitted to be shadowed by function-like macros. This means that if you intend to use a reserved name for someting without external linkage, you must first #undef it or enclose the name in parentheses to prevent it from being treated as a macro.

// Including this header may result in the definition
// of a function-like macro named "strategy".
#include <string.h>

// Must enclose in parentheses to prevent misinterpretation
// as function-like macro.
static void (strategy)();

As of C11, identifiers matching the following regular expressions may not be used for symbols with external linkage. (The list is given in section 7.31: “Future library directions”.) There are also reserved names for type definitions and macros, but I won’t list them here.

Pattern Header
cerfc?[fl]?, cexp2[fl]?,
cexpm1[fl]?, clog1[0p][fl]?,
clog2[fl]?, c[lt]gamma[fl]?
complex.h
is[a-z].*, to[a-z].* ctype.h, wctype.h
atomic_[a-z].* stdatomic.h
str[a-z].* stdlib.h, string.h
mem[a-z].* string.h
wcs[a-z].* string.h, wchar.h
cnd_[a-z].*, mtx_[a-z].*,
thrd_[a-z].*, tss_[a-z].*
thread.h

It may come as a surprise that the C language reserves identifiers like strong, island, and together, but it does.

Bonus chatter: Windows header files have historically not been conscientious about avoiding these reserved names. We’re trying to do better for new headers, but not everyone has gotten the memo.

Update: Added special “internal double-underscore” rule.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

14 comments

Discussion is closed. Login to edit/delete existing comments.

  • Thiago Macieira

    Paper N2625 in the ISO C Language working group has a bigger list, more comprehensive than Raymond's above. For example, the following declarations are also disallowed:

    #define ENTOMOLOGY 1 // reserved
    #define SIGNIFICANT_RESULTS 1 // reserved
    #define TIME_TO_EAT 1 // reserved
    #define ATOMIC_WEIGHT .000001f // reserved
    #define INTERESTING_VALUE_MIN 0 // reserved

    The paper goes on to make a proposal that changes the effect of having a reservation: instead of making programs illegal / undefined for simply having...

    Read more
  • Daniel Roskams

    The C standard is kind of a joke that no one actually follows. I don’t have hard numbers, but I bet I could count on one hand the number of non-trivial programs on my computer that 100% comply to the C standard. Even people that postulate about writing “standards compliant” or “portable” etc. code have probably named a variable “total” or “strict” or “structureSize” or “isvalid” at some point.

    • George Tokmaji

      Normal variables don’t have external linkage, and the C11 rules in the table only applies to symbols with external linkage.

  • Letao Wang

    These rules are kinda yikes. They barely ever get mentioned, let alone taught systematically. Compilers don’t seem to care to enforce them either. They are like traps that will happily let people walk in, and everything will feel fine until a random day when it explodes. Either that or they are “rules in name only”, the kind that everyone violates and there are no consequences.

  • Michael Spam

    How many C++ programs have a variable name called total . Millions? Had no idea it was reserved!

  • James Touton · Edited

    Note the wording for the double-underscore prohibition:

    > Each identifier that contains a double underscore __ […]

    The prohibition covers all names that contain a double underscore anywhere in the identifier, not just at the start.

    • Lukas Mai · Edited

      Yeah, that’s why “the rules for C happen to match the C++ rules” is not quite true. C++ reserves identifiers that contain double underscores anywhere, but C does not. In other words,

      int a__b;

      is perfectly valid C, but uses a reserved identifier in C++.

  • Henke37

    I’d argue that the windows api counts as part of the implementation, and as such is allowed to use the reserved names.

    • MGetz

      And yet the windows headers include macros for min and max which clobber the standard std::min and std::max creating what is technically undefined behavior unless the dev defines NOMINMAX. Which is also why that’s a standard define in all my projects, because while the macro versions are technically faster they are also technically undefined behavior.

  • Adam Rosenfield

    Even if they’d be within their rights to do so, I’d wager that the ISO C committee would try to avoid introducing certain new functions like `strafe` that could potentially collide with identifiers being used by real code that didn’t follow the rules.

    • 紅樓鍮

      Good C code nowadays always use “poor man’s namespaces” by prefixing the library name to global names, and really the C language standard should start doing the same, even if old names cannot be changed.

      • aaaaaa 123456789 · Edited

        And that’s exactly what happened: C23 uses stdc_ for a bunch of functions it introduces.

    • Dustin Howett

      We’re definitely behind on fixing that one 😅