Close Menu
GeekBlog

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    Diablo 5 Just Got Announced, and This Time You Don’t Stop the Apocalypse, You Survive It

    September 14, 2026

    Meta Spent Three Years Flattening Management for AI. Now It’s Rebuilding the Layer It Cut.

    September 14, 2026

    A Hacker Ran Hundreds of AI Agents at Once. GreyNoise Says It Breached 440 Servers in Four Hours.

    September 14, 2026
    Facebook X (Twitter) Instagram Threads
    GeekBlog
    • Home
    • Mobile
    • Tech News
    • Blog
    • Gaming
    • Smartwatch
    • How-To Guides
    • AI & Software
    Facebook
    GeekBlog
    Home»Blog»How to Use an Enum Inside a Struct in C and C++
    Blog

    How to Use an Enum Inside a Struct in C and C++

    Ethan CaldwellBy Ethan CaldwellSeptember 9, 202612 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    C++ source code displayed on a computer monitor
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link

    To use an enum inside a struct in C or C++, declare the enum type (either before the struct or nested inside it), then add a member of that enum type to the struct the same way you would add an int field. In C the enum lives in the global namespace even when you write it inside the struct body, while in C++ a nested enum is scoped to the struct and you refer to it as StructName::EnumName. The rest of this guide walks through both languages, the typedef patterns you will see in real C code, initialization, switching on the member, size and packing, and how to serialize enum fields safely.

    Quick answer: Declare enum Color { RED, GREEN, BLUE }; then struct Pixel { enum Color color; int x; int y; }; in C (or typedef the enum to drop the enum keyword). In C++ prefer a nested enum class Color { Red, Green, Blue }; inside the struct and access it as Pixel::Color::Red. Initialize with struct Pixel p = { RED, 0, 0 }; and read it back with a switch.

    Enums inside structs show up everywhere: state machines, protocol headers, configuration records, game entities. The mechanics are simple, but scoping differences, member size, and what happens when the struct hits the disk trip people up regularly. Every sample below compiles cleanly with gcc -Wall -Wextra or g++ -Wall -Wextra -std=c++17.

    Declaring an enum inside a struct in C

    C has a flat tag namespace. You can physically write the enum declaration inside the struct body, but the enum tag and every enumerator still land in the enclosing scope, so there is no scoping benefit. Most C code declares the enum first, then uses it as a member type.

    #include <stdio.h>
    
    enum ConnState {
        CONN_IDLE,
        CONN_CONNECTING,
        CONN_OPEN,
        CONN_CLOSED
    };
    
    struct Connection {
        int fd;
        enum ConnState state;
        unsigned retries;
    };
    
    int main(void)
    {
        struct Connection c = { -1, CONN_IDLE, 0 };
        c.state = CONN_CONNECTING;
        printf("state = %d\n", (int)c.state);
        return 0;
    }

    Notice the enum ConnState state; member. In C you must repeat the enum keyword unless you typedef the type. The enumerator names use a CONN_ prefix because they are global; without a prefix, a second enum in another header with an IDLE enumerator would be a redefinition error.

    The typedef pattern

    Most production C code uses one of two typedef styles. The first names the enum tag and the typedef separately; the second uses an anonymous enum.

    /* style 1: tag plus typedef, works with forward declarations */
    typedef enum ConnState {
        CONN_IDLE, CONN_CONNECTING, CONN_OPEN, CONN_CLOSED
    } ConnState;
    
    /* style 2: anonymous enum, shortest form */
    typedef enum {
        LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR
    } LogLevel;
    
    typedef struct {
        ConnState state;   /* no enum keyword needed */
        LogLevel  level;
        int       fd;
    } Connection;

    Style 1 is the safer default in header files because you can forward declare enum ConnState; elsewhere if you need to. Style 2 is fine for enums that stay private to one translation unit.

    Writing the enum literally inside the struct body

    This is legal C, and you will see it in older code, but it gives you nothing over declaring it outside:

    struct Packet {
        enum PacketKind { PKT_DATA, PKT_ACK, PKT_NAK } kind;
        unsigned short length;
    };
    
    /* still valid in C: the enum escaped to file scope */
    enum PacketKind k = PKT_ACK;

    In C++ that last line fails to compile, because PacketKind is now Packet::PacketKind. That difference is the single most common surprise when a C header gets included from a C++ file.

    Declaring an enum inside a struct in C++

    C++ gives you real scoping. A nested enum belongs to the struct, and its enumerators are reached through the struct name. Two flavors exist: the unscoped nested enum and the scoped enum class.

    #include <cstdio>
    
    struct Pixel {
        enum class Color : unsigned char { Red, Green, Blue };
    
        Color color = Color::Red;
        int x = 0;
        int y = 0;
    };
    
    int main()
    {
        Pixel p;
        p.color = Pixel::Color::Blue;
    
        Pixel q{ Pixel::Color::Green, 10, 20 };   // aggregate init
    
        if (q.color == Pixel::Color::Green) {
            std::puts("green");
        }
        return 0;
    }

    Recommended for you:

    How to Use the TikTok for Developers Documentation
    Blog·Sep 9, 2026

    How to Use the TikTok for Developers Documentation

    Three things are worth calling out. First, : unsigned char fixes the underlying type, which controls sizeof and lets you forward declare the enum. Second, enum class enumerators do not implicitly convert to int, so printf("%d", p.color) is an error; you need static_cast<int>(p.color). Third, default member initializers (= Color::Red) mean a default constructed Pixel is never in an indeterminate state, which is a real advantage over the C version.

    Unscoped nested enum in C++

    If you drop the class keyword, the enumerators are reached as Pixel::Red and convert to integers freely, which is handy for bit flag enums you combine with |. For everything else, enum class is the better default because the compiler catches accidental mixing of unrelated enums.

    Initializing and assigning the enum member

    The table below collects the initialization forms that compile in each language.

    FormCC++
    struct S s = { CONN_OPEN, 3 }; positionalYesYes (aggregate)
    struct S s = { .state = CONN_OPEN }; designatedYes (C99)Yes (C++20, in declaration order)
    Default member initializer Color c = Color::Red;NoYes (C++11)
    Zero init = {0} sets the enum to enumerator value 0YesYes
    Assign a raw int s.state = 2;Compiles (warning with -Wall in some cases)Error, needs a cast

    Designated initializers are the most readable choice for structs with several enum members because the order of fields stops mattering in C. C++20 accepts them too but insists that the designators appear in declaration order.

    Tip: Make the zero enumerator a meaningful “unset” or “none” value (CONN_IDLE = 0, Color::None). Then memset, calloc, and = {0} all produce a struct whose enum member is valid instead of accidentally meaning something like “open”.

    Switching on the enum member

    A switch over the member is the idiomatic way to dispatch on it, and it is where compiler warnings earn their keep. With -Wall, GCC and Clang emit -Wswitch, which warns when a switch on an enum has no default and misses an enumerator. Leave the default out on purpose so that adding a new enumerator later produces a warning at every switch you forgot to update.

    const char *conn_state_name(const struct Connection *c)
    {
        switch (c->state) {
        case CONN_IDLE:       return "idle";
        case CONN_CONNECTING: return "connecting";
        case CONN_OPEN:       return "open";
        case CONN_CLOSED:     return "closed";
        }
        return "unknown";   /* reached only for out of range values */
    }

    The trailing return after the switch matters. An enum variable can legally hold a value that matches no enumerator (after a bad cast or a corrupted read from disk), and without that line the function would fall off the end, which is undefined behavior. In C++ the same pattern works with case Pixel::Color::Red: labels. If you use exceptions for invalid states, the guide on how to catch exceptions in C++ covers the throw and catch side.

    sizeof, alignment and packing

    In C, an enum is an integer type large enough to hold all its enumerators, and in practice GCC, Clang and MSVC make it 4 bytes (the size of int) on mainstream targets unless you use -fshort-enums or a packed attribute. In C++ you can pin the underlying type explicitly, and the compiler uses exactly that size.

    #include <stdio.h>
    #include <stdint.h>
    
    enum Kind { K_A, K_B, K_C };
    
    struct Loose  { uint8_t tag; enum Kind kind; uint8_t flags; };
    struct Tight  { uint8_t tag; uint8_t kind;   uint8_t flags; }; /* store enum in a byte */
    
    int main(void)
    {
        printf("Loose = %zu, Tight = %zu\n", sizeof(struct Loose), sizeof(struct Tight));
        return 0;
    }

    On a typical 64 bit Linux build this prints Loose = 12, Tight = 3, because the 4 byte enum forces 4 byte alignment and padding on both sides. Two portable fixes exist. In C, store the value in a uint8_t field and cast when you read it ((enum Kind)s.kind). In C++, declare enum class Kind : uint8_t and the member itself becomes one byte. Bitfields (enum Kind kind : 2;) also work in both languages, though GCC warns if the enumerator range does not fit and MSVC treats enum bitfields as signed unless you specify an underlying type.

    Warning: Do not rely on __attribute__((packed)) to shrink an enum inside a struct that another compiler or another language will read. The layout is compiler specific, and packed structs can produce misaligned pointers that crash on ARM.

    Serializing a struct with an enum member

    Enum values are implementation defined integers, so writing the raw struct bytes with fwrite ties your file format to one compiler’s size and endianness. The robust approach is to serialize the enum as an explicit fixed width integer and validate it on read.

    #include <stdio.h>
    #include <stdint.h>
    
    enum Kind { K_A = 0, K_B = 1, K_C = 2, K_COUNT };
    
    struct Record { enum Kind kind; int32_t value; };
    
    int write_record(FILE *fp, const struct Record *r)
    {
        uint8_t k = (uint8_t)r->kind;
        uint32_t v = (uint32_t)r->value;
        return fwrite(&k, 1, 1, fp) == 1 && fwrite(&v, 4, 1, fp) == 1;
    }
    
    int read_record(FILE *fp, struct Record *r)
    {
        uint8_t k; uint32_t v;
        if (fread(&k, 1, 1, fp) != 1 || fread(&v, 4, 1, fp) != 1) return 0;
        if (k >= K_COUNT) return 0;          /* reject values that are not enumerators */
        r->kind = (enum Kind)k;
        r->value = (int32_t)v;
        return 1;
    }

    The K_COUNT sentinel is a common trick: it is always one past the last real enumerator, so the range check stays correct as you add values. Assign explicit numbers to every enumerator that ever reaches disk or the network, and never reorder them. For text formats, write the enumerator name using the switch based lookup from the previous section and parse it back with strcmp. The companion tutorial on how to write a file in C covers the file handling side in depth, and if you are reading records back into a container in C++, see how to store text file data into a vector in C++.

    When you format enum values into fixed width file names or log lines, you will often want zero padded numbers; that is covered in how to add leading zeros in C and C++.

    Troubleshooting

    “unknown type name ‘ConnState'” in C

    You used the enum name without the enum keyword and without a typedef. Either write enum ConnState state; or add typedef enum ConnState ConnState; after the declaration.

    “‘Red’ was not declared in this scope” in C++

    The enum is nested or scoped, so qualify it: Pixel::Color::Red for enum class, Pixel::Red for an unscoped nested enum. Inside member functions of Pixel you can shorten it to Color::Red.

    “redeclaration of enumerator ‘IDLE'”

    Two C enums in the same scope share an enumerator name, because C enumerators are global. Prefix the enumerators (CONN_IDLE, TASK_IDLE) or, in C++, switch to enum class.

    “enumeration value ‘CONN_CLOSED’ not handled in switch”

    This is -Wswitch doing its job. Add the missing case. Resist adding a default: just to silence it, since that hides the same bug the next time the enum grows.

    Struct size is larger than expected

    The enum member is 4 bytes and forces alignment padding. Use enum class Kind : uint8_t in C++ or store the value in a uint8_t field in C, then reorder members from largest to smallest.

    Recommended for you:

    How to Embed TikTok Videos on a Website or App
    Blog·Sep 9, 2026

    How to Embed TikTok Videos on a Website or App

    Frequently asked questions

    Can I declare an enum inside a struct in C?

    Yes, the syntax compiles, but C does not scope it. The enum tag and its enumerators become visible in the surrounding scope exactly as if you had declared the enum outside the struct. Most C programmers declare the enum first, add a typedef, and then use it as a member type for clarity.

    What is the difference between enum and enum class inside a struct in C++?

    Both are scoped to the struct. An unscoped nested enum exposes its enumerators as Struct::Value and converts implicitly to int. An enum class adds another level (Struct::Enum::Value) and blocks implicit conversion, so you cannot compare or assign it to unrelated enums or integers without a cast.

    How big is an enum member inside a struct?

    In C on common compilers it occupies 4 bytes, the same as int, and aligns on a 4 byte boundary. In C++ you can specify the underlying type (for example : uint8_t) and the member shrinks to that size. Padding around the member depends on the neighboring fields, so order members by size.

    Can I use the enum member in a switch statement?

    Yes, and that is the recommended way to branch on it. Compile with -Wall so the compiler warns when a switch without a default misses an enumerator. Always return or handle values after the switch, since an enum variable can hold an out of range integer after a cast or a bad read.

    Is it safe to write a struct containing an enum directly to a file?

    Only if the same compiler, same flags and same architecture read it back. The enum’s size and byte order are implementation details. For anything that crosses machines or versions, convert the enum to a fixed width integer with explicit values, and validate the number against the known range when reading.

    How do I print an enum member’s name instead of its number?

    C and C++ do not store enumerator names at runtime. Write a small function with a switch that returns a string literal for each enumerator, or keep a static const char *names[] array indexed by the enum value, guarded by a range check against a COUNT sentinel.

    The bottom line

    Putting an enum inside a struct is a one line job in both languages: declare the enum, add a member of that type. The decisions that matter are around it. In C, prefix your enumerators, typedef the enum, and make the zero value mean “none.” In C++, nest an enum class with a fixed underlying type and give the member a default initializer.

    Treat the enum member as an integer with a contract whenever it leaves the process: assign explicit values, serialize as a fixed width type, and range check on the way back in. Do that, and enums inside structs stay one of the cheapest, clearest tools you have for modeling state.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Telegram Email Copy Link
    Previous ArticleHow to Deploy AngularJS on DreamHost (SFTP, .htaccess, HTTPS and Caching)
    Next Article How to Link Instagram to a Facebook Page (App, Page Settings and Business Suite)
    Ethan Caldwell

      Ethan Caldwell is GeekBlog's resident Apple specialist, covering the entire Apple ecosystem - iPhone, iPad, Mac, Apple Watch, AirPods and the software that ties them together. A longtime iOS user and gadget collector, Ethan tracks Cupertino's every move, breaking down Apple keynotes, A- and M-series chip benchmarks, iOS feature updates and the rumor mill into clear, practical takes that help readers decide whether the latest Apple hardware is worth the upgrade.

      Related Posts

      11 Mins Read

      South Carolina vs Michigan: Which State Is Better to Live In?

      11 Mins Read

      Are There Cordless Vacuums With Replaceable Batteries?

      12 Mins Read

      How to Use Parabolic SAR (Stop and Reverse) for Day Trading

      10 Mins Read

      Michigan vs Illinois: Which State Is Better to Live In?

      11 Mins Read

      California or Florida: Which State Is Better to Move To?

      11 Mins Read

      Best Front End Development Books to Learn From in 2026

      Top Posts

      Gal Gadot’s Lawyers Spent Six Months on One AI Clause. Then SAG Called Them for Pointers.

      September 2, 20264 Views

      Every iPhone Camera Ranked in 2026 (Best to Worst)

      July 6, 20263 Views

      The Mesh Router Placement Strategy That Finally Gave Me Full Home Coverage

      September 9, 20262 Views
      Stay In Touch
      • Facebook

      Subscribe to Updates

      Get the latest tech news from FooBar about tech, design and biz.

      Most Popular

      How to Change HEIC to JPG on iPhone, Mac, Android and Windows (No Software Needed)

      September 3, 20266 Views

      Gal Gadot’s Lawyers Spent Six Months on One AI Clause. Then SAG Called Them for Pointers.

      September 2, 20265 Views

      How to Spot AI Generated Images in 2026 (The Old Tricks Stopped Working)

      September 3, 20263 Views
      Our Picks

      Diablo 5 Just Got Announced, and This Time You Don’t Stop the Apocalypse, You Survive It

      September 14, 2026

      Meta Spent Three Years Flattening Management for AI. Now It’s Rebuilding the Layer It Cut.

      September 14, 2026

      A Hacker Ran Hundreds of AI Agents at Once. GreyNoise Says It Breached 440 Servers in Four Hours.

      September 14, 2026

      Subscribe to Updates

      Get the latest creative news from FooBar about art, design and business.

      HEICJPG.online - Convert HEIC to JPG online
      Facebook
      • About Us
      • Contact us
      • Privacy Policy
      • Disclaimer
      • Terms and Conditions
      • Editorial Policy
      • Cookie Policy
      © 2026 GeekBlog

      Type above and press Enter to search. Press Esc to cancel.