I'm more interested with the terrible ideas implemented in Delphi (or maybe FPC) as mentioned by @440bx and @Thaddy. Can you please list more items that you think they're 'ugly'? I think it has educational value. Sorry of being off topic, or should I start a new thread?
Note:
Although I have 'Hero Member' status above my avatar picture but because I'm an autodidact hobbyist programmer, I missed many 'formal' knowledge about programming.
Even though I really have little interest in discussing language design (the subject has a tendency to lead to rather unproductive discussions), I do feel I owe at least one example. Therefore here is one, likely the first Borland design atrocity that made it into the Pascal language, is the so called "writeable constants".
In a declaration such as:
const
// AONTS = ArrayOfNullTerminatedStrings
AONTS : array[0..1] of pchar = ('first char array', 'second char array');
The pointers in the array aren't constant but the compiler has to treat them as if they were at compile time, however at runtime, it treats them as variables.
At runtime you can assign a new pointer to AONTS[0] and AONTS[1] and the pointer, while it needs to be a pchar, it isn't required to point to a constant string, it can point to a buffer whose contents may/can change at anytime. This in turn causes a problem, if the programmer attempts to change the original string an exception will occur since the pointer is pointing to an initialized constant in a non-writeable segment in the exe but, if the pointer is changed to point to some buffer (and if the buffer is large enough) then the operation will succeed.
In C++ (where they implemented it correctly), the pointers are variables and/but, those variables are [must be] pointers to constants. The compiler will NOT accept an element of that array being assigned a pointer which is not a pointer to a null terminated character constant (as it should.) Therefore _all_ attempts to modify the target of the pointer will result in an exception (as it should.)
Writeable constants = a terrible idea. The reason they implemented them is because allowing the construct
var
SomeVar : integer = 10;
would have required changes in the code generator. Specifically, if the above construct had been allowed then, if it had been used to declare a
local variable (instead of a global variable) then the compiler would have had to generate code to initialize the local variable, since they didn't want to enhance the code generator to do that, they came up with "writeable constants". That way, they created a construct which cannot be used for local variables thus eliminating the need to generate code and breaking language symmetry. _bad_, really _bad_.
I have mixed feelings providing only one example when there are actually quite a few but, I know where providing such a list leads and I am not interested in getting there, since there is no chance that any of these "deficiencies" will be addressed, much less corrected.
Here is a complete example that shows one of the many problems created by writeable constants
{$MODE OBJFPC }
program PointersToConstantCharArrays;
const
constchar = 'a character constant array';
aconstarray : array[0..1] of pchar = ('first', 'second');
type
rec = record
intfield : integer;
charfield : pchar;
end;
const
// this works because there is no array of pointers to the character array
// i.e, constchar is itself the pointer to the character array.
recconst : rec = (intfield : 0; charfield : constchar);
const
// this does NOT work
// it should NOT force the use of the @ operator, it won't compile without it
// and with it, the output is wrong. This results in charfield holding a
// pointer to a pointer to characters instead of a pointer to characters.
// IOW, charfield is a ppchar instead of, what it should be, a pchar.
recconst2 : array[0..1] of rec
= (
(intfield : 0; charfield : @aconstarray[0]),
(intfield : 0; charfield : @aconstarray[1])
);
var
AnArray : array[0..31] of char;
begin
writeln(recconst.charfield);
// the following two statements output garbage because charfield is a pointer
// to a pointer to characters. charfield is NOT a pointer to characters which
// is what writeln expects.
writeln(recconst2[0].charfield); // outputs garbage
writeln(recconst2[1].charfield);
AnArray[0] := 'h'; AnArray[1] := 'e'; AnArray[2] := 'l';
AnArray[3] := 'l'; AnArray[4] := 'o'; AnArray[5] := #0;
// this assignment should not be possible
aconstarray[0] := AnArray; // AnArray isn't a pointer to a string of
// character constants. The compiler should NOT
// allow this but, it has to.
writeln(aconstarray[0]);
// it is possible to write to aconstarray[0][0]
aconstarray[0][0] := 'f';
writeln(aconstarray[0]);
// but the same thing on [1][0] will result in a SIGDEV
aconstarray[1][0] := #0;
end.
The ambiguity (and inconsistency) is clearly visible between aconstarray[0] and aconstarray[1]. The compiler should _not_ have allowed aconstarray[0] to be a pointer to a non constant.