-
Notifications
You must be signed in to change notification settings - Fork 14.8k
Open
Labels
cclang:frontendLanguage frontend issues, e.g. anything involving "Sema"Language frontend issues, e.g. anything involving "Sema"
Description
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
Labels
cclang:frontendLanguage frontend issues, e.g. anything involving "Sema"Language frontend issues, e.g. anything involving "Sema"