June 14, 2026·4 min read·engineeringcstandardsportability

Making plain int bit-fields signed in C

N3902 proposes removing a signedness exception that has become harder to explain as C adds more ways to name types.

A type alias ought to preserve what a type means. C bit-fields complicate that expectation because plain int can acquire unsigned semantics in a bit-field declaration. Robert Seacord proposes removing that exception in N3902, dated June 8 and listed in the WG14 document log on June 14. The proposal targets C2y. It would make plain int bit-fields signed, including when a typedef or typeof supplies the type.

C was my first language, and its continued importance to interoperability is a large part of why I still care about its details. A special rule that changes the meaning of a familiar type deserves scrutiny. Here the useful simplification is to make the declared type determine signedness consistently, while keeping explicit unsigned declarations available.

The ambiguity predates the new type machinery

The earlier defect report 315, submitted in 2005, illustrates how far the difficulty extends. It asks whether implementations that permit additional bit-field types must preserve the signedness of ordinary objects with those types. Its discussion considers char, short, long and long long. The committee response leaves these cases to implementation-defined behavior.

That history matters because a declaration has two separate jobs. It chooses a type, and it limits a member to a width. Programmers can reasonably expect the width to constrain the values without making the type name mean something different. An exception to that expectation becomes harder to explain when the name comes through an alias. The programmer reading the declaration then needs information outside the immediate definition to know what arithmetic means.

The defect report also discusses integer promotions. It explains that the representable values, restricted by the width for a bit-field, determine whether promotion to int is possible. Signedness consequently affects more than the appearance of a declaration. It participates in the rules governing expressions that consume the member.

Existing implementations already expose the choice

GCC documents its bit-field behavior. Plain int fields default to signed, and -funsigned-bitfields can change that choice. Its dialect option documentation describes the corresponding signed and unsigned switches. Those options apply when a declaration omits both signedness keywords.

A small declaration makes the practical issue visible:

struct state {
    int delta : 4;
    unsigned int flags : 4;
};

The first member leaves signedness to the current C implementation rules. The second expresses its unsigned intent in the declaration. N3902 would remove the first choice from conforming implementations of the proposed rule. It does not require every author to insert a signedness keyword.

The existence of a compiler option also explains why checking only a compiler name is insufficient. Build configuration contributes to the program being compiled. A project that depends on unsigned plain fields has a dependency on that configuration, even if the dependency never appears in a header. Such a project needs a deliberate decision about the declarations before changing its language assumptions.

C++ has already resolved the corresponding question

C++ core issue 739 records a closely related problem with typedefs and template arguments. The earlier wording permitted implementation-defined signedness for several plain integer bit-field types. An example asks about the conditional operator when its operands include plain, signed and unsigned fields. That question depends on whether the operands have the same type.

The February 2012 resolution removed the permission. Its compatibility note says plain int bit-fields are signed. The rationale names inconsistent template specializations as the problem and extends the consistent rule to non-dependent types. The issue moved to defect-report status at the October 2012 meeting.

This is a useful precedent for C because it identifies a type consistency problem independently of coding style. Requiring more explicit spelling would be a different policy. N3902 discusses that alternative but chooses the existing signed meaning of int. My preference is for that smaller conceptual rule. It reduces the amount of context a reader needs without making ordinary int declarations invalid.

Signedness leaves other layout decisions open

The GCC implementation documentation separately assigns bit-field allocation order and the ability to straddle a storage-unit boundary to the ABI. It also leaves structure member alignment to the ABI. A signedness change therefore does not establish a portable serialized structure format. These are different properties with different consequences for interoperability.

That distinction is particularly relevant to how I think about C. Calling conventions and layout are often the reason another language needs a C definition at all. Removing one ambiguity makes the definition easier to reason about, but it supplies no evidence that two targets arrange every member identically. A shared header still needs an agreed ABI when its object representation crosses a binary boundary.

N3902 remains a proposal, with separate poll questions about adoption for C2y and application to earlier standards. Code that already spells signed int or unsigned int keeps its intended signedness under the proposed direction. Code depending on unsigned plain int fields is the compatibility case that needs attention. The useful change is a more consistent type rule for future C, and the transition question remains part of the committee work.