Hacker News new | ask | show | jobs
by f33d5173 2 days ago

  $ gcc -Wall -Werror -x c - << EOF
  void f(int x[static 1]){}int main(){f(0);}
  EOF
  <stdin>:1:37: error: null passed to a
      callee that requires a non-null argument
      [-Werror,-Wnonnull]
    1 | void f(int x[static 1]){}int main(){f(0);}
      |                                     ^ ~
  <stdin>:1:12: note: callee declares array
      parameter as static here
    1 | void f(int x[static 1]){}int main(){f(0);}
      |            ^~~~~~~~~~~
  1 error generated.
It can be done, though it usually isn't.
2 comments

`int x[static 1]` isn’t exactly intuitive when one wants to define a reference to an integer. Nor is this practical given a dynamic array of integers. Or a single integer field in a struct.
Can you do that with a dynamic array? If not, it's pretty severely limited (unless you mean that literally forbidding dynamic memory is usually not done, which I guess it's true outside of some embedded code but not a particularly meaningful statement).
The syntax is rather confusing. This is not an array of length 1, but rather a pointer which points to a memory segment which is at least 1 integer long. In other words, any array of any length (>=1) would be a valid argument to this function. "static" here means "don't do the normal thing where you totally ignore the length of an array argument to a function (which is, like usual, really just a pointer)". "static" was chosen because the keyword was available rather than because it was a particularly descriptive name.
I see. Can you use `static 0` for a non-null empty array, or does that run into issues around what it means to allocate 0 bytes? I feel like the distinction between "empty but safe to use" and "not safe to use, so you need to check first" is a pretty important part of what most of the solution look like in languages that don't have this problem. Being able to statically know that something is non-empty is nice, but it's not quite the same as being forced to check if it's valid.
I can understand that this a somewhat practical solution to the problem of codebase is all C and all I have is a C compiler, so there is merit to using the static keyword here. But it is also unwieldy and used rarely, so I do not think it is comparable to Rust’s borrow rules in any meaningful sense.
> Can you do that with a dynamic array?

Yes.

  #include <stdio.h>
  
  #if __has_include(<stdcountof.h>)
  #include <stdcountof.h>
  #else
  #define countof(a) (sizeof (a) / sizeof *(a))
  #endif
  
  void
  foo(int n, int a[static 1][n])
  {
   printf("sizeof *a: %zu\n", sizeof *a);
   printf("countof *a: %zu\n", countof(*a));
  }
  
  int
  main(int argc, char *argv[])
  {
   int array[argc];
   foo(countof(array), &array);
   return 0;
  }

  $ ./foo                            
  sizeof *a: 4
  countof *a: 1
  $ ./foo 2 3
  sizeof *a: 12
  countof *a: 3
Tested using Apple clang 21.0.0 and gcc 15.2.0.

The syntax for using passed VM arrays is stilted; you're operating on a pointer to an array, which can get confusing and is error prone. Because of the semantics for array passing and need for backward compatibility it's too easy to get it wrong without the compiler catching mistakes and complaining. Though, the C2y _Countof operator is required to error when used on a non-array, so using _Countof(a) instead of _Countof(*a) will fail. (GCC and clang also have warning diagnostics that work for the fallback countof macro.) And there's no way (or no easy way?) to ask compilers to inject automatic bounds checking when operating on arrays, at least outside non-production debugging modes like ASan.

But C is getting there, slowly.