April 30, 2026·6 min read·engineeringcgccboundsc2y

GCC 16 gives pointer members an explicit element count

GCC 16 extends counted_by to structure pointer members. The compiler can use the count for bounds information, but callers must maintain a strict relationship between the fields.

GCC 16.1 arrives on April 30, with additional C2y support and an extension to the GNU counted_by attribute. The C change notes list pointer members as a newly supported use of that annotation. A structure can now tell GCC that one member gives the number of elements accessible through another member. The practical question is how to maintain that relationship throughout the lifetime of the structure.

C was my first language, and after thirty years I remain interested in changes that make its implicit obligations available to tools. Much of the language maintains its importance through interoperation. A declaration that explains a pointer and count relationship can help a compiler understand code whose meaning previously depended on conventions outside the type declaration. I would still review the annotation as a claim made by the programmer, with specific obligations attached to it.

The count describes elements

The GCC 16.1 attribute documentation permits counted_by on a flexible array member or a pointer field in C. The argument names an integer field in the same structure. That field gives the number of elements in the flexible array or the array addressed by the pointer. GCC can use the relationship to improve object-size information, compile-time diagnostics and facilities such as the array bounds sanitizer and __builtin_dynamic_object_size.

The unit matters. An element count and a byte count coincide for a character array, which can conceal a naming error when an example changes to another element type. I would name a count field for the elements it represents. If an interface also needs a byte length, I would want the code to show which quantity it is passing at each boundary. The annotation does not make two differently defined lengths interchangeable.

This declaration illustrates the pointer case using the documented GNU attribute syntax.

#include <stddef.h>

struct sample_buffer {
    size_t count;
    [[gnu::counted_by(count)]] int *samples;
};

The declaration says the pointer addresses an array with at least count available elements. It does not acquire storage or establish ownership of that storage. My review would ask where the pointer originates and which operation ends its validity. Those questions remain necessary even when the count accurately describes the allocation at initialization. Bounds information is useful only within the lifetime of the storage to which it refers.

The manual says the attribute is available only in C and is ignored in C++. That is relevant for headers shared across language boundaries. I would not infer equivalent checking from using the same declaration in each compiler mode. A project evaluating this annotation should identify which translation units actually receive the compiler behavior the annotation requests.

Initialization and updates have stronger requirements

GCC requires the count to be initialized before the first reference to the associated field. It also requires at least that many elements to remain available whenever the relationship applies. The documentation assigns responsibility for those conditions to the programmer. Violating them can produce warnings and makes the bounds sanitizer and dynamic object-size results undefined.

Pointer fields add a particularly consequential requirement. The pointer and its count can change only by changing the whole structure at the same time. A routine that updates the count and pointer through separate member assignments does not meet that documented rule. I would inspect update functions before adding the attribute to a structure that already exists, since common-looking mutations may need a different organization.

This differs from the flexible-array example in the same documentation. In that example, GCC uses the latest preceding count assignment when evaluating a reference to the array member. The manual explicitly says this behavior does not apply to pointer fields. Treating the pointer extension as identical to the flexible-array case would therefore miss a contract that directly affects initialization and resizing code.

I would begin an experiment with a structure initialized as a unit and no subsequent independent mutations of the pair. That would give a small example whose relationship is easy to inspect. Only after establishing the compiler behavior there would I move to code with replacement allocations or ownership transfers. The additional cases should each explain how they preserve the documented relationship.

A buffer that can shrink its accessible extent needs particular care. Suppose the count still advertises the old extent while the pointer refers to smaller storage. The annotation would state a property that is false at that point. I would require a complete account of the transition before accepting a resize implementation. Successful allocation alone does not establish that the structure now describes the new allocation correctly.

The permitted pointer types have boundaries

The manual excludes function pointers and pointers to structures or unions containing a flexible array member. It allows non-void incomplete structure or union types if the type can be completed before the pointer is first referenced. Those restrictions matter for a generic container library, where a declaration may hide more about the element type than a local buffer example does.

A void pointer is permitted, but GCC treats each element as one byte and warns when -Wpointer-arith is enabled. That rule gives a specific interpretation to a count attached to untyped storage. I would document that unit in an interface exposing such a member, since the spelling of the pointer type does not communicate the eventual element type of every consumer.

GCC also treats a negative value in a signed count member as zero. This does not establish that the application intended an empty array. An application may still need to reject a negative external value before constructing the structure. I would keep input validation separate from compiler interpretation of the annotation, especially when a value arrives through a parser or another interface outside the structure definition.

These details make the attribute more useful to evaluate than a general promise of better safety. The manual identifies the types it accepts and the states the program must maintain. A targeted test can exercise each relationship the application actually uses. I would avoid judging adoption by how many declarations acquire the annotation, since that number says little about whether their update paths satisfy the contract.

C2y support also strengthens compile-time expression checks

The release notes list static assertions in expressions among the new C2y capabilities. They also permit unspecified and variably modified arrays as type names in generic associations. These are language changes with their own mode and compatibility considerations. They do not make counted_by a standard C2y attribute, since the notes list that attribute under GNU extensions.

That separation matters for a portability policy. I would record the required compiler extension independently of the requested language dialect. A source file can use a newer standard mode while still depending on implementation-specific behavior. An upgrade review should be explicit about both, so a different compiler rejecting or ignoring an annotation does not become an unexplained surprise.

GCC also says it implements diagnostics for cases that had undefined behavior in earlier language modes and now violate constraints. That change can make formerly accepted code fail during compilation under the newer rules. I regard earlier diagnosis as useful, but would still examine each diagnostic against the actual source. Changing an option to recover a previous build result would not explain whether the program relied on behavior the newer mode prohibits.

Adoption needs tests of the relationship

For an annotated pointer member, I would start with a test covering an ordinary access within the declared element count. A separate case should exercise the bounds checking configuration the project intends to use. The test needs to establish the observed diagnostic or runtime behavior under that configuration. Merely compiling the attribute syntax would show acceptance of the declaration without demonstrating the checking behavior that motivated it.

The update tests should inspect the structure as a unit. I would include any transition that replaces the underlying storage, together with the path that handles a failed replacement. If the previous buffer remains owned after failure, the count and pointer must continue to describe that previous buffer. The annotation makes that requirement more visible, but it cannot choose the failure policy for the application.

A useful review record would state the allocation source, the meaning of the count and the permitted update operation in ordinary prose beside the relevant code. Each part answers a different question about the relationship. GCC 16 can use the declaration to improve its understanding of the object, while the surrounding code still needs to establish that the declared relationship holds after every supported transition.