Imagine the humble pie C would have to eat.
C beat out Pascal in the 80s for a lot of reasons, many of them not technical.
Pascal had the correct format for strings, the one you mentioned and the one used by all modern languages.
Imagine if after 40 years C would start using Pascal strings :-))
https://www.cs.virginia.edu/~evans/cs655/readings/bwk-on-pas...
Been digging through old newsgroups, and it sounds like one of the big reasons C won out over Pascal was because it was easier to find good C compilers in the 1980s compared to good Pascal compilers.
It didn't help that Pascal didn't have a usable standard, which is what led to articles like "Why Pascal is Not Brian Kernighan's favorite language", which also didn't help.
Edited to add: there was also some amount of antiestablishment fervor in the microcomputer community, and Pascal was for better or worse perceived as a somewhat ivory-tower language.
Want to write the string 'fat dog' on line 12 column 10? With Pascal you'd have to write a routine in assembly and call it. With C you use a trivial macro. Pascal was also generally very very slow. Almost as slow as basic which had better strings than Pascal.
MoveTo(10, 12);
DrawString('fat dog');
You could do worse.Anyway, Pascal was designed for teaching, and Modula-2 was designed for production code and systems programming.
UCSD Pascal, and Object Pascal also took many of their ideas from Modula-2.
The first ISO standard to address this wasn't until 1990, with yet a new dialect called Extended Pascal, which of course wasn't compatible with the various earlier system-specific dialects, and anyway was way too late for Pascal to "win" over C.
See Wikipedia for confirmation: "Brian Kernighan, who popularized the C language, outlined his most notable criticisms of Pascal as early as 1981 in his article "Why Pascal is Not My Favorite Programming Language". The most serious problem Kernighan described was that array sizes and string lengths were part of the type, so it was not possible to write a function that would accept variable-length arrays or even strings as parameters."
C only became portable by bringing UNIX alongside via POSIX.
Then there were all those dialects, K&R C, Small-C, RatC, DBS C,...
Not really. The most crucial insight is that your string references should be fat pointers, which is not what Pascal does. Look at Rust's &str for the state of the art. Other choices are less important, but having string references be fat pointers is a huge boon. It would probably have seemed profligate in the 1980s, but it was the right choice.
The other three are first class types, tagged unions, and closures.
Standards committee says no.
It works fine. The length has the same size as an address, the fat pointer being a pair of addresses is technically equivalent but has worse performance in practice so you should make it an (address, length) pair instead of course.
You can't exceed this length because of the pigeonhole principle, basically just arithmetic.
Is it worse though? Serious question, I’ve been wondering for quite a bit. IME processing a string from the start is easier with a (start, end) pair (one operation instead of two when you chop off things from the start). You can’t use those in standard C because it reserves the right to blow up on pointer subtraction essentially randomly (ptrdiff_t overflow is UB, and the only requirement for ptrdiff_t is that it hold 2^15-1; C89 doesn’t even tell you what the maximum allowed value is), but that’s not a performance problem, and not a problem at all in a different language.
ANS Forth[1], released 1994, used (addr, len) pairs throughout, eliminating most uses of “counted” (Pascal) strings in Forth-83 and earlier.
"Proper" strings, arrays, slices, tagged unions, etc... would need to be integrated into the language (unless you want to end up with a mess like C++).
https://github.com/uecker/noplate
Certainly not production ready.
For arrays you may be right, possibly a fat pointer like suggested by Denis himself.
But you can get run-time bounds checking already: https://godbolt.org/z/jhcavobYj
Support for statically detecting problems is also (slowely) improving.
For instance in a slice type, does this have a start and end pointer, or a start pointer and size? Does the pointer or size come first? Is size the number of elements, or number of bytes? Alignment? Padding? Etc...
Apparently you mean something by "struct types" other than the well known C feature. Can you elaborate?
For example, Zig's slice type is a 'builtin struct' which contains a pointer and size, the builtin error union type is made of an error code and the actual result type. These things are not provided by the Zig standard libary, but instead built into the language.
In contrast, C's builtin types are all 'primitives' (pointers, integers and floats) - with the arguable exception of VLAs, which have been made non-mandatory in C11.
Go/Java and generics for example. C++ and auto or closures. Javascript and async/await ("just learn regular Javascript async patterns").
The thing is C could have had a string type that knows its length since day one, with known performance tradeoffs, and also allow for everybody to build their own type in the more rare cases where it's needed.
And most of buffer overflow and string handling bugs would have been avoid, as most of the programs using them don't need the performance or special handling of C strings (or don't need them where the bugs occur, e.g. when reading some configuration file).
Maybe yes, maybe no. Walter Bright, the creator of D, has a nice article on the subject [1]. I think Zig's approach which allows slices to decay to pointers and pointers+lengths to create slices is ideal. The real benefit of slices is the implicit bounds checking which some C folks might object to since one of the appeals of C is no "hidden magic".
So working with C or C++, it is.
https://digitalmars.com/articles/C-biggest-mistake.html https://dlang.org/articles/d-array-article.html