D has variable length arrays as a builtin type. They are extremely useful.
C VLAs are a terribly clumsy design. Here's the right way to do it in C:
That was basically Dennis Ritchies' proposal as well: Dennis Ritchie, Variable-Size Arrays in C, September 1990, http://jclt.iecc.com/Jct22.pdf#page=5
IIRC, one of the problems there is how to generalize it to, e.g., struct members. (Maybe you can speak to that.) The approach adopted by C99[1], being based on dependent typing, was I think better conceptually. But despite dependent typing being awesome, it manifestly was and still remains a tough sell to compiler implementors, so VLAs/VMTs have languished in C and have yet to be fully developed. I think I'd agree C would have been better off with the approach you suggest. Adoption likely would have been quicker, and it could have been implemented in full almost immediately.[2] The consolation prize is that hopefully C will soon see some progression on the VMT front, specifically the proposals under discussion on the committee for forward-referencing in function parameter VMTs and VMT struct array members. The final piece of that puzzle would be addressing the scary syntax for VLA passing, which feels error prone (C2y countof operator semantics help a little bit).
[1] Tom MacDonald, Variable Length Arrays, December 1989, http://jclt.iecc.com/Jct13.pdf#page=67
[2] Ironically GCC already had nascent VLA support, which may have helped nudge the C99 committee toward that instead of adopting Ritchie's approach. But the fat pointer approach has a more obvious and straight-forward implementation.
One issue with D's design is that it uses
char a[..]
as syntax for a pointer, not an array. C has the same confusing issues in prototypes where it allows char[] notation for pointers, but this as it is widely seen as a mistake.
I do not agree that C VLAs are a terribly clumsy design. Especially from the point of dependent typing, C's design is the right one, e.g. you can express something such as
void transpose(int X, int Y, double (out)[X][Y], const double (in)[Y][X]);
If you only have existential types and not the fully dependent form, then this is not possible.