https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121275
Bug ID: 121275 Summary: Extend _Countof() to work with array parameters Product: gcc Version: 16.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: foss+...@alejandro-colomar.es Target Milestone: --- Hi! I'll work on this once Martin's patch lands on trunk. <https://inbox.sourceware.org/gcc-patches/3d92a9ba50d4f04b561010fc6f35597babae05a6.ca...@tugraz.at/> Below is a draft of a proposal we're discussing in the C Committee. Have a lovely day! Alex --- Name alx-0054r2 - _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 <a...@kernel.org> Cc: Martin Uecker <uec...@tugraz.at> Cc: Christopher Bazley <chris.bazley.w...@gmail.com> Cc: Kees Cook <keesc...@chromium.org> Cc: Linus Torvalds <torva...@linuxfoundation.org> Cc: Aaron Ballman <aa...@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. See also - 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.