a := [5]int32{0, 1, 2, 3, 4} // Go
let mut array: [i32; 5] = [0, 1, 2, 3, 4]; // Rust a := [5]int32{0, 1, 2, 3, 4} // Go
let mut array: [i32; 5] = [0, 1, 2, 3, 4]; // RustThe problem is that it doesn't use them much, and gets rid of them as soon as possible:
int a[5] = {1, 2, 3, 4, 5, 6};
printf("%d\n", a[8]);
in clang, this warns, by default, on both misuse sites "excess elements in array initializer" and "array index is past the end of the array". Which is good.However things go downhill as soon as you pass the arrays downstack:
#include <stdio.h>
static void foo(int *a) {
printf("%d\n", a[8]);
}
static void bar(int a[]){
printf("%d\n", a[8]);
}
static void qux(int a[6]){
printf("%d\n", a[8]);
}
int main() {
int a[5] = {1, 2, 3, 4, 5};
foo(a);
bar(a);
qux(a);
}
no warning anywhere, even under "-Wall -Weverything". #include <stdio.h>
static void qux(int a[static 6]){
printf("%d\n", a[8]);
}
int main() {
int a[5] = {1, 2, 3, 4, 5};
qux(a);
}
I get, test.c:9:5: warning: array argument is too small; contains 5 elements, callee requires at least 6 [-Warray-bounds]
I did not enable any warning flags at all, this is just default Clang on a Mac.If you want to catch the a[8] inside the function you need something else.
Note that you could
static void qux(int n, int a[n])
but this changes the interface somewhat and there are lots of reasons why you might want to avoid VLAs in C.Proper array types are not present in C and they're unlikely to be added at any point in the future, so if the conversation is "the array data and length need to be combined into a single semantic entity", then the conversation is no longer about C, but the starting point for choosing another language.
The int a[static 6] syntax made it into the language because it's a small enough change that it's not disruptive to existing uses of C, but it still has some benefit (I've used it, I've seen its benefits).
I'd even be happy if the standard just included a macro like:
#define ARRAY(T) struct { T *ptr, size_t len }
Along with a few functions for working with such types.
Although that would be kind of unwieldy to use in C, since you would need a typedef to actually use any specific types.
So long, you can look at my experiments using a struct type here: https://github.com/uecker/noplate/
The typedef thing will go away with C2X where you do this in place.
VLAs had a poor cost/benefit because they're just too awkward to use. 20 years experience with D shows that the [] syntax knocks it out of the park.
Any combination of static/fat arrays can be made for multidimensionality. For example,
[..][3] array of 3 fat pointers
[3][..] fat pointer to 3 element array
[3][3] array of 3 3-element arrays
[..][..] fat pointer to array of 3 fat pointers
etc.My other questions are how do you create one of these fat pointers, how do you get the length, etc. This sounds to me like VLAs, but with a bigger change to the language, but unable to handle multidimensional arrays. VLAs, for all their faults, at least fit into the language for users well enough that people accidentally use them all the time (not necessarily a good thing).
T[3][4][..] a;
> length a.length static void qux(int n, int *a);
static void qux(int n, int a[]);
static void qux(int n, int a[*]);
static void qux(int n, int a[n]);
static void qux(int n, int a[12345]);
Weird interface problems mainly come when you have VLA types behind pointers.That's a mild understatement.
int a[static 3][4]
You only need to put static on the outermost type, the one that decays into a pointer. Same thing applies if you need restrict: int a[static restrict 3][4]
which is ugly but gets the job done. Think of int a[3][4], when used as a function parameter, as “pointer to int[4]” (the 3 disappears completely), and think of int a[static 3][4] as “pointer to first of at least 3 int[4]”.(Not trying to defend C here, just explaining how C works.)
static void foo(int (*a)[6]) {
printf("%d\n", (*a)[8]); // warning: array index 8 is past the end of the array (which contains 6 elements)
}
int main() {
int a[5] = {1, 2, 3, 4, 5};
foo(&a); // warning: incompatible pointer types passing 'int (*)[5]' to parameter of type 'int (*)[6]'
}
This is all vanilla C89, so it works even with very old compilers, so long as they're conformant.