Right. Also it might it sound like array-to-pointer decay is forced onto the programmer. Instead, you can take the address of an array just fine without letting it decay. The type then preserves the length.
int main() {
int a[3];
return foo(a);
}
> gcc test.c
> ./a.out
Oops.D: int foo(int[] a) { return a[5]; }
int main() {
int[3] a;
return foo(a);
}
> ./cc array.d
> ./array
core.exception.ArrayIndexError@array.d(1): index [5] is out of bounds for array of length 3
Ah, Nirvana!How to fix it for C:
int foo(int (*a)[6]) { return a[5]; }
int main() {
int a[3];
return foo(&a);
}
Or for run-time length: int foo(int n, int (*a)[n]) { return (\*a)[5]; }
int main() {
int a[3];
return foo(ARRAY_SIZE(a), &a);
}
/app/example.c:4:38: runtime error: index 5 out of bounds for
type 'int[n]'
https://godbolt.org/z/dxx7TsKbK\* int foo(int n, int (*a)[n]) { return (\*a)[5]; }
int main() {
int a[3];
return foo(ARRAY_SIZE(a), &a);
}
That syntax is why array overflows remain the #1 problem with C bugs in shipped code. It isn't any better than: int foo(size_t n, int* a) { assert(5 < n); return a[5]; }
int main() {
int a[3];
return foo(ARRAY_SIZE(a), a);
}
as the array dimension has to be handled separately from the pointer.Contrast with how simple it is in D:
int foo(int[] a) { return a[5]; }
int main() {
int[3] a;
return foo(a);
}
and the proof is shown by array overflow bugs in the wild are stopped cold. It can be that simple and effective in C.The holy grail is runtime access to the length, which means an array would have to be backed by something more elaborate.