Skip to content

Extend _Countof() to work with array parameters #150953

@alejandro-colomar

Description

@alejandro-colomar

Hi!

Below is a draft of a proposal for the C Committee which is under discussion at the moment. I've also proposed this feature in GCC, where I'll implement it soon. This is the natural evolution of the _Countof operator.

Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121275

Cc: @AaronBallman

Have a lovely day!
Alex


Name
	alx-0054r3 - _Countof array parameters

Principles
	-  Uphold the character of the language.
	-  Keep the language small and simple.
	-  Avoid ambiguities.
	-  Pay attention to performance.
	-  Codify existing practice to address evident deficiencies.
	-  Do not leave features in an underdeveloped state.
	-  Avoid quiet changes.
	-  Enable secure programming.

	And from previous charters:

	C11:
	-  No invention, without exception.

	C23:
	-  APIs should be self-documenting when possible.

Category
	Do the Right Thing(tm).

Author
	Alejandro Colomar <alx@kernel.org>

	Cc: Martin Uecker <uecker@tugraz.at>
	Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
	Cc: Kees Cook <keescook@chromium.org>
	Cc: Linus Torvalds <torvalds@linuxfoundation.org>
	Cc: Aaron Ballman <aaron@aaronballman.com>

History
	<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0054.git/>

	r0 (2025-07-27):
	-  Initial draft.

	r1 (2025-07-28):
	-  tfix.
	-  Require that the array parameter is const-qualified.
	-  Add 'Future directions'.
	-  Mention 'Avoid quiet changes'.

	r2 (2025-07-28):
	-  tfix.

	r3 ():
	-  Link to GCC proposal to extend _Countof.

See also
	-  GCC proposal to extend _Countof:
	   <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121275>

	-  GCC diagnostic proposal:
	   -Wsizeof-array
	   <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121270>

	-  GCC dialect flag proposal:
	   -fconst-array-parameters
	   <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121271>

	This proposal depends on:

	-  n2906 (2022-01-04; Uecker, "Consistency of Parameters Declared as Arrays (updates N2779)")
	   <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2906.pdf>

	This proposal would benefit from:

	-  n3433 (2025-01-25; Bazley, "Alternative syntax for forward declaration of parameters")
	   <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3433.pdf>

	This proposal conflicts with:

	-  n3656 (2025-07-27; Na, "Dependent attributes")
	   <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3656.pdf>

Rationale
	Historically, the C language had a way (the one and only way) to
	express that a parameter to a function is in reality an array.
	The ABI is that of a pointer, but usage is (almost) identical to
	an array (except for sizeof, and other operators that take into
	account the type of the operand)

		void foo(size_t n, int arr[n]);

	People have complained about them, mainly because they're
	dangerous.  People have tried to solve this with alternatives
	(fat pointers, std::array, std::vector, std::span, etc.) in C++.
	C has remained free of those things (for good) so far.  Now
	people try to add alternatives in C (n3656, Clang), disregarding
	all the principles mentioned above.

	A solution in C must integrate with the rest of the language,
	and that means using array syntax (as we've done forever in C),
	not changing the rules for parsing C code, not inventing
	completely new stuff at all.

	The solution has been there all the time; we only need to read
	all the information that is already present in most code: array
	parameter bounds.  Programmers are already conscious enough that
	they provide this information even if the compiler ignores it;
	it's good for their own readability (and some compilers already
	use it for diagnostics, including GCC).

	Now, we have _Countof().  This allows us to go farther than GCC.
	We can not only use that information for diagnostics, but we can
	use it for our actual code:

		void
		foo(size_t n, int a[const n])
		{
			for (size_t i = 0; i < _Countof(a); i++)
				a[i] = 0;
		}

	where _Countof(a) evaluates to 'n', the Right Thing.

	This would be directly usable in APIs where the size is visible
	before the array parameter, which is true in many internal APIs
	in projects.

	For the libc APIs, where this is not possible (due to the order
	of parameters), we'd need to standardize GCC's forward
	declaration of parameters, as proposed by n3433.

    const
	The array parameter must be const-qualified, to prevent it from
	being modified (advanced), which would turn the length
	information obsolete by the time _Countof() is used.

    Better than "dependent" attributes
	The approach with array notation and _Countof() not only enables
	some static analysis.  It allows writing better code.

	Let's say we want to call snprintf(3) safely.  I personally use
	the following wrappers (T for truncation):

		#define STPRINTF(s, fmt, ...)                         \
		(                                                     \
			stprintf(s, countof(s), fmt __VA_OPT__(,) __VA_ARGS__) \
		)


		[[gnu::format(printf, 3, 4)]]
		int
		stprintf(int size;
		    char s[restrict size], int size,
		    const char *restrict fmt, ...)
		{
			int      len;
			va_list  ap;

			va_start(ap, fmt);
			len = vstprintf(s, size, fmt, ap);
			va_end(ap);

			return len;
		}

		[[gnu::format(printf, 3, 0)]]
		int
		vstprintf(int size;
		    char s[restrict size], int size,
		    const char *restrict fmt, va_list ap)
		{
			int  len;

			len = vsnprintf(s, size, fmt, ap);
			if (len == -1)
				return -1;
			if (len >= size) {
				errno = E2BIG;
				return -1;
			}

			return len;
		}

	These allow one to call STPRINTF() on arrays like this:

		char  buf[PATH_MAX];

		if (STPRINTF(buf, "/proc/%d/", pid) == -1) {
			goto fail;

	Not needing to specify the array size at call site at all.
	By extending the _Countof operator to work on array parameters,
	we could do the same exact thing with them:

		int
		bar(int size, char buf[const 100], pid_t pid)
		{
			if (STPRINTF(buf, "/proc/%d/", pid) == -1) {
				return -1;

			...

			return 0;
		}

	This is only possible with _Countof(), and only possible with
	array notation.  _Countof() will never support pointers; that's
	a promise, by design.  Thus, this will not work with the
	attributes proposed in n3656.

    Arbitrarily complex prototypes
	This feature can be used for more complex functions, such as
	[v]seprintf(), proposed in alx-0049.

		[[gnu::format(printf, 3, 0)]]
		char *
		vseprintf(char *const restrict p, const char *restrict end;
		    char p[const restrict p ? end - p : 0],
		    const char end[restrict 0],
		    const char *restrict format, va_list ap)
		{
			int        len;
			ptrdiff_t  size;

			if (p == NULL)
				return NULL;

			len = vsnprintf(p, _Countof(p), fmt, ap);

			if (len == -1)
				return NULL;
			if (len >= _Countof(p)) {
				errno = E2BIG;
				return NULL;
			}

			return p + len;
		}

	Such a simple feature (4 lines added to the standard), provides
	very strong safety improvements to the C language, ruling out
	entire classes of bugs from the language; not only by enabling
	static analysis, but even better: by not being able to write
	them in the first place, with wrapper macros that call
	_Countof() as appropriate.

Prior art
	This reuses old C syntax with the meaning it always had, even if
	the standard ignores such syntax.

	And for the _Countof() extension, Martin and I are working on
	implementing this in GCC.

Future directions
    -Wsizeof-array
	We could make it a constraint violation to use sizeof(array).
	Now that we have _Countof(), it's safer to use it even for the
	size in bytes of an array, as

		_Countof(array) * sizeof(array[0])

	As it prevents accidentally using pointers.  And it would even
	work with array parameters, unlike sizeof().

	It's a breaking change, but it's not a silent one, so we're
	covered by 'Avoid quiet changes'.

    -fconst-array-parameters
	In the future, we could make all array parameters
	const-qualified.  I'd suggest compilers to add a flag that turns
	the dialect into that, which eventually turn into the default.

	This would also be a breaking change, but again we're covered by
	'Avoid quiet changes'.

    _Generic(), typeof()
	The combination of the two items above will make array
	parameters quite close to actual arrays.  Actually, almost
	indistinguishable, if not by _Generic() in combination with
	typeof().  We may want to tweak that too afterwards.  We'll see.

Proposed wording
	Based on N3550.

    6.5.4.5  The sizeof, _Countof, and alignof operators
	@@ Constraints, p1
	...
	 The _Countof operator shall only be applied to
	 an expression that has a complete array type,
	-or to the parenthesized name of such a type.
	+or to the parenthesized name of such a type,
	+or to a <b>const</b>-qualified array parameter of specified size.
	...

	@@ Semantics, p5
	 The _Countof operator yields
	 the number of elements of its operand.
	 The number of elements is determined from
	 the type of the operand.
	+If the operand is an array parameter,
	+the number of elements is determined from
	+the array type used in its declaration.
	 The result is an integer.
	 If the number of elements of the array type is variable,
	 the operand is evaluated;
	 otherwise,
	 the operand is not evaluated
	 and the expression is an integer constant expression.

Metadata

Metadata

Assignees

No one assigned

    Labels

    cclang:frontendLanguage frontend issues, e.g. anything involving "Sema"

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions