C Enum Sizes; or, How MSVC Ignores The Standard Once Again

Written by ettolrach, 2026-02-23.

My friend ran into an interesting bug, and then I ran into a very similar issue myself. I was surprised to not find many references to it online, so I decided to make a little post myself so I can reference it later.

In C we can declare an enum like so:

enum Composer {
        Beethoven = 1,
        Tchaikovsky = 2,
        VaughanWilliams,
};

C calls variants enumerators. We can give our enumerators an enumeration constant using the = character. If we don't give the first enumerator (that's Beethoven above) an enumeration constant, then its constant will be 0. All subsequent enumerators without declared constants will have a constant that's 1 greater than the previous enumerator. So, VaughanWilliams is guaranteed to have a constant of 3.

But what's the type of these numbers? Well, we could be explicit and declare it (this is a new feature in C23, so a lot of websites are wrong and claim that it's impossible to specify a type):

enum Composer: int {
        Beethoven = 1,
        Tchaikovsky = 2,
        VaughanWilliams,
};

And the C standard provides this answer for when we don't specify a type (§ 6.7.3.3, 2):

All enumerations have an underlying type. The underlying type can be explicitly specified using an enum type specifier and is its fixed underlying type. If it is not explicitly specified, the underlying type is the enumeration’s compatible type, which is either char or a standard or extended signed or unsigned integer type.

So, a standard-compliant compiler can choose our Composers enum to have an underlying type of char or long long, both are compliant decisions.

GCC chooses 32 bits and correctly expands to 64 when an enumerator's constant is set to a value larger than 232 - 1. Clang does the same. We can confirm this with this code where we set VaughanWilliams to 232, a value not representable using an int:

#include <stdio.h>

enum Composer {
        Beethoven = 1,
        Tchaikovsky = 2,
        VaughanWilliams = 4294967296,
};

int main() {
        enum Composer comp = VaughanWilliams;
        printf("Vaughan Williams: %lu\n", comp);
        printf("size: %lu\n", 8*sizeof(enum Composer));
}

// Output:
// Vaughan Williams: 4294967296
// size: 64

And what happens when we run this code on MSVC?

// Output:
// Vaughan Williams: 0
// size: 32

Oh no. That is code running on Visual Studio 2026 18.3.1, MSVC 14.50, the latest at the time of writing. MSVC also doesn't support the underlying type annotation, even after enabling the "experimental" ISO C23 support in the VS project options.

But hey. This couldn't cause a runtime error, right?

So to summarise, if you're writing a C program or library and want an enum with constants larger than 232 - 1, if you're using Clang or GCC, then great! If you're using MSVC, then you should just use a long long (or an unsigned version if you don't need negatives) and constants. You should probably consider making your program more type-safe by using structs which wrap values anyway. If you're using a C library (which is pre-compiled, so a static or shared library) and you know the library supports MSVC compilation, then you can safely assume all enums are 32-bit. If it supports Linux, then it likely compiles using Clang or GCC, in which case the enum will expand its size correctly. In fact, if you're using Rust and interacting with a C FFI like I was, then bindgen can figure out the appropriate type in std::ffi::c_xxx (e.g. c_int or c_ulong).

Comments

Please be respectful. Any comment which I find objectionable will be removed. By submitting a comment, you agree to the privacy policy.

* required.

Hidden.

No comments yet.