Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
jval

Chocolate Strife: A_FireSigil

Recommended Posts

Graf Zahl said:

That's still idiotic and can be handled with a compile option.


Compile options are orthogonal to language standards. But compile options are basically where these things get fixed first. I was reading an interesting article about pointer aliasing yesterday, where basically -fno-strict-aliasing is necessary for a huge amount of real-world C code because the aliasing rules are too strict

http://blog.regehr.org/archives/1307

Graf Zahl said:

But as things go this undefined-ness is not just kept for old features, they still add it to new features making the entire language standard one big mess.


I'm going to be charitable and assume that defining shifts on negative integers is not top of their priority list.

Share this post


Link to post
Jon said:

I'm going to be charitable and assume that defining shifts on negative integers is not top of their priority list.



I'm in the opposite camp. Every single undefined feature is a problem.
And there's a lot of code that depends on this working like a sane person would expect - even in Doom ports.

Share this post


Link to post

C standards have always been too laissez faire. I like to think that the modern standards have improved slightly but it's certainly not much.

There was a good reason for it when the first ANSI standard came out. C's strength was in being a "midlevel" language, "portable assembly" that could be compiled down to hardware instructions for any language. They wanted something that could target any CPU out there and the way they did it was by making a ton of stuff ambiguous.

It was appropriate at the time because: CPUs were very slow and it was essential to squeeze every cycle out of them for performance; computers were way more heterogeneous and even basic assumptions like "everything uses 2's complement arithmetic" did not hold everywhere; the entire field of computer security did not exist yet.

Nowadays it's a very dated and inappropriate notion. I'd much rather see a well defined language where behaviour is reliable and consistent. The people on the C standards committee don't seem to understand the need for this or perhaps aren't bold enough to go through with it. I'd much rather see ambiguities eliminated that the addition of pointless features like complex number types (why they even bothered with that I have no idea)

Share this post


Link to post

The standards committees are probably concerned more with retaining backwards compatibility than fixing language fundamentals. Considering that GCC and Clang support options like -std=c11, it might be a little bit silly.

Fixing C may just be a bit too confusing for developers and effort is probably better spent making sure that projects like Rust (and maybe Go? I'm not sure if it's suitable for the same systems-level programming that C and Rust target) don't have the same pitfalls, and then encouraging people to use Rust rather than C.

Share this post


Link to post
chungy said:

The standards committees are probably concerned more with retaining backwards compatibility than fixing language fundamentals. Considering that GCC and Clang support options like -std=c11, it might be a little bit silly.


Such options are a far better way to ensure that old code still compiles, rather than have a standard with more holes than Swiss cheese.

chungy said:

Fixing C may just be a bit too confusing for developers and effort is probably better spent making sure that projects like Rust (and maybe Go? I'm not sure if it's suitable for the same systems-level programming that C and Rust target) don't have the same pitfalls, and then encouraging people to use Rust rather than C.


May I laugh? How are these languages supposed to get off the ground? There's no momentum. D is also suffering from the same problem.

The only language that got some push recently was Swift, and the sole reason for that is that many Apple developers are the same type of people as Apple users - looking up to their iGod and asking it for enlightenment.

What I dislike about these languages is that they all have this insane need to reinvent the wheel instead of improving it. And that will be their ultimate undoing. The reason Java, C++ and C# are the most popular is very much because they replicate most of C's basic syntax.

Share this post


Link to post

C ABI compatibility has a lot of good reasons for Go and Rust leapfrogging the chicken/egg problem. Being able to write and use libraries in either (or any of) the languages with a common ABI is a huge win.

As for Swift: I'm not sure how you see it as a credible contender. It lacks said ABI compatibility and the only non-Apple support it has is a very rudimentary Linux port... it's pretty well worse off.

Share this post


Link to post
chungy said:

As for Swift: I'm not sure how you see it as a credible contender. It lacks said ABI compatibility and the only non-Apple support it has is a very rudimentary Linux port... it's pretty well worse off.


If you do some iOS programming you'd know why I see it as a credible contender. I can't say I like the language, but it's increasingly replacing Objective-C as Apple's main development language.

Share this post


Link to post

Ha, so basically, it takes:

{
    for (int i = 0; i < 4; ++i)
        std::cout << i*1000000000 << std::endl;
}
and optimizes it as
{
    for (int i = 0; i < 4000000000 ; i += 1000000000)
        std::cout << i << std::endl;
}
and since an int cannot be as big as 4000000000, the termination condition can never happen, so it is optimized to:
{
    for (int i = 0; ; i += 1000000000)
        std::cout << i << std::endl;
}
A different optimization approach, such as unrolling the loop, wouldn't have the bug undefined behavior that is totally logical and it's your fault for writing bad code:
{
    std::cout << 0 << std::endl;
    std::cout << 1000000000 << std::endl;
    std::cout << 2000000000 << std::endl;
    std::cout << 3000000000 << std::endl;
}

Share this post


Link to post

Just another piece of proof that GCC developers are boneheaded idiots.
The first rule of optimization should be 'Never break code on the default setting'!
And if some aggressive optimizations are wanted they should be opt-in, not opt-out. And by that I mean, not being lumped into some generic catch-all switch.

Stuff like this creates far more problems than it solves. Funny enough, the compiler recognizes it but instead of reverting to safe code it just emits a warning and throws up, making the optimization perfectly worthless.

Share this post


Link to post

Removing a large portion of the undefined portions of the C standard has the potential to break many programs, especially ones which are pointer heavy. Plus, programmers may complain that C was turned into a holding hands language.

fraggle said:

I'd much rather see ambiguities eliminated that the addition of pointless features like complex number types (why they even bothered with that I have no idea)


Complex number types are for engineering/scientific purposes, however those in the scientific fields generally use other languages instead of C especially if they were made for math. If you are doing signal processing, electronics, and such, you might actually want complex numbers. Complex numbers are optional when it comes to C however.

Share this post


Link to post

The reasoning of a compiler optimizer writer:

  • The standard says anything can happen when undefined behavior is specified
  • I can abuse this to make code faster
  • Code being faster is the most important thing in the universe because it is the thing that I personally study.
  • Thus, I shall do everything and anything technologically possible in order to exploit all such undefined behavior holes for the point of making code faster.
  • If this causes issues in software, it is always somebody else's fault.
Or tl;dr, optimizer writers are hostile actors who are out to actively cause security problems, destroy legacy systems, cost organizations actual money in the redevelopment of what was thought to be stable software, and just in general, act like antisocial douches. They expect programmers to be impossibly precise machines who are aware of every single jot and tittle of the standard on every line of code they write and how every single line interacts with every single other line.

If we had programmers capable of the sort of focus and automaton-like thought processes expected of them by optimizer writers, we would in fact need neither compilers nor optimizers, because we would all screech binary modem noise into a microphone and the machine code would just appear in a file as if God spake the program into existence.

Share this post


Link to post

As long as the compiler warns about it, then it's the programmer's responsibility. Also, there are different optimization levels.

Share this post


Link to post
GhostlyDeath said:

Removing a large portion of the undefined portions of the C standard has the potential to break many programs, especially ones which are pointer heavy.

I don't see it as a problem: you define a new standard (eg. "C 2016"), eliminate as many of the ambiguities as possible and move on.

All I'm talking about is removing ambiguities. If your code doesn't work then it must have been relying on undefined behavior already - it's already broken. The only people it really affects is people writing compilers, and that's fine - if they were targeting some weird system that really needed that undefined behavior (like a system that uses 1's complement arithmetic) they can just say that their compiler targets the older, more lax version of the standard, if they really need to.

GhostlyDeath said:

Plus, programmers may complain that C was turned into a holding hands language.

VGA said:

As long as the compiler warns about it, then it's the programmer's responsibility.

This is exactly the kind of stupid attitude that leads to insecure software.

People have this idea that it's fine to make any kind of dangerous, badly designed tool just as long as you find a way to blame the user when they hurt themselves. It's an irresponsible point of view and the people who defend it are actively promoting dangerous practices.

The C standard is full of examples: functions like strcpy(), sprintf(), etc. shouldn't exist. They're smart enough to have removed gets() now, but they should have removed those others as well. The only reason they haven't is that they can blame the user when those functions go wrong. But it's not a defence: the people with the power to remove those functions should be campaigning to remove them, not shifting the blame to the people hurt by the bad design they try to defend.

Complex number types are for engineering/scientific purposes, however those in the scientific fields generally use other languages instead of C especially if they were made for math.

Well, yes, but fundamentally I don't see why it ever needed to be part of the language standard. This is something that can be implemented as a custom library in an afternoon.

Quasar said:

Or tl;dr, optimizer writers are hostile actors who are out to actively cause security problems, destroy legacy systems, cost organizations actual money in the redevelopment of what was thought to be stable software, and just in general, act like antisocial douches. They expect programmers to be impossibly precise machines who are aware of every single jot and tittle of the standard on every line of code they write and how every single line interacts with every single other line.

I partially agree or at least, I sympathize. The thing is, these behaviors were made undefined deliberately so that compiler writers could exploit the ambiguity to get better performance. It's the standard which allows them to act this way. Nowadays I'd rather have a standard that acknowledges that ambiguities do more harm than good, and eliminates as many of them as possible. Every one of those "jots and tittles" is effectively a landmine and the standard is a field of them; clear the field, reduce the cognitive overhead of things that a C programmer needs to know, and we can write more safe and correct code.

Share this post


Link to post
VGA said:

As long as the compiler warns about it, then it's the programmer's responsibility.


That would be a valid point if there was One Compiler that everyone used. The Compiler, and none other. But that's not the case. Programmer doesn't get any warning from the compiler he uses; therefore it's not his responsibility if it breaks for some other person. Right?

Share this post


Link to post
Quasar said:

Or tl;dr, optimizer writers are hostile actors who are out to actively cause security problems, destroy legacy systems, cost organizations actual money in the redevelopment of what was thought to be stable software, and just in general, act like antisocial douches.


Strangely enough it ALWAYS seems to be GCC running into these situations. Sadly this seems to be one field where 'free software' actually implies 'free of any responsibility for one's doing'.

Share this post


Link to post
Quasar said:

If we had programmers capable of the sort of focus and automaton-like thought processes expected of them by optimizer writers, we would in fact need neither compilers nor optimizers, because we would all screech binary modem noise into a microphone and the machine code would just appear in a file as if God spake the program into existence.

This image was hilarious. If you ever feel like writing a novel let me know, I'll read it.

Share this post


Link to post
Graf Zahl said:

I'm in the opposite camp. Every single undefined feature is a problem.
And there's a lot of code that depends on this working like a sane person would expect - even in Doom ports.

Agreed. The undefined signed shift bewildered me when I learned it, because every processor I have ever written software for can do logical shifts, arithmetic shifts, rotates, rotates with carry, left, right, whatever. C is supposed to be a level above assembler, but here it falls flat. Those shifts are very useful, and there's just no way to do them where they are efficient and portable, except in a few isolated cases.

VGA said:

As long as the compiler warns about it, then it's the programmer's responsibility. Also, there are different optimization levels.

The program is always the programmer's responsibility. Providing a toolkit that works properly across the range of possible inputs is the language creator's responsibility. Being able to turn that program, written in that language, into an executable is the compiler's responsibility. That the compiler outputs warnings and errors is a nicety (or a nuisance, depending on the occasion.) Of course it should complain if it cannot make sense out of your code. But, you don't listen to every compiler's pleas, and you don't consider your program done because the compiler is silent.

Quasar said:

The reasoning of a compiler optimizer writer:

  • The standard says anything can happen when undefined behavior is specified
  • I can abuse this to make code faster
  • Code being faster is the most important thing in the universe because it is the thing that I personally study.
  • Thus, I shall do everything and anything technologically possible in order to exploit all such undefined behavior holes for the point of making code faster.
  • If this causes issues in software, it is always somebody else's fault.
Or tl;dr, optimizer writers are hostile actors who are out to actively cause security problems, destroy legacy systems, cost organizations actual money in the redevelopment of what was thought to be stable software, and just in general, act like antisocial douches. They expect programmers to be impossibly precise machines who are aware of every single jot and tittle of the standard on every line of code they write and how every single line interacts with every single other line.

If we had programmers capable of the sort of focus and automaton-like thought processes expected of them by optimizer writers, we would in fact need neither compilers nor optimizers, because we would all screech binary modem noise into a microphone and the machine code would just appear in a file as if God spake the program into existence.

Whoa, harsh, dude! Yeah, the optimizer was simply wrong when it optimized entryway's code. That's clearly a bug in the optimizer. I would like to think that optimizer writers are concerned with accuracy first, and speed second (but, not it that case, huh?).

You have to appreciate what they are attempting to do, though. I am quite amazed that an optimizer could have the insight to consider that change. Sure, it should have rejected it. But, that's some impressive AI going on there! It has to follow the entire loop, determine dependencies, figure out what can be removed from the loop, or eliminated altogether. I've been considering writing an optimizer for a language I've been playing with, and the thought scares me! That's some deep code, there!

Share this post


Link to post
kb1 said:

Agreed. The undefined signed shift bewildered me when I learned it, because every processor I have ever written software for can do logical shifts, arithmetic shifts, rotates, rotates with carry, left, right, whatever. C is supposed to be a level above assembler, but here it falls flat. Those shifts are very useful, and there's just no way to do them where they are efficient and portable, except in a few isolated cases.


Actually, I know one exception, which is the 6502. Of course nobody would use C on such a system to begin with...

Share this post


Link to post

It is quite amusing reading this thread as someone who has been involved with building the C++ standard (C++ concepts in particular). It seems like you all want undefined behavior to be removed except for the cases which you haven't had an issue with. What I mean by that is removing undefined behavior would imply that all array accesses need to be checked, but pretty much every C++ developer I know of would agree that doing that is stupid. So what often happens when people complain about C++ is it really boils down to "they should fix what *I* think is stupid." Or even worse "all compilers should do exactly like *my* compiler."

Now the C compatibility stuff in C++ is certainly debatable, but I'm sure C++ wouldn't be where it is today if it didn't have it. C compatibility is very much C++'s biggest mistake as much of the undefined behavior that people actually run into is a result of programming habits that derive from C. Not that it's defined in C either, but the C programmer notion of "knowing what something translates to in machine code" often gets people into traps like casting between incompatible types or abusing unions.

As for optimizers, I'm always amused at the people who claim that "modern optimizers are great, there's no need for hand made ASM" and then turn around and claim that aggressive optimizations are "boneheaded." So basically the compiler should be all knowing and only aggressively optimize code in hot code paths which are written correctly (which is impossible to determine statically in real code). You're welcome to get the behavior you desire by turning the optimizer off. Then I'm sure the compiler will do what's logical when presented with undefined behavior. ;)

C++ doesn't get everything right, but most of the points I see here are pretty naive.

Share this post


Link to post
Blzut3 said:

As for optimizers, I'm always amused at the people who claim that "modern optimizers are great, there's no need for hand made ASM" and then turn around and claim that aggressive optimizations are "boneheaded." So basically the compiler should be all knowing and only aggressively optimize code in hot code paths which are written correctly (which is impossible to determine statically in real code). You're welcome to get the behavior you desire by turning the optimizer off. Then I'm sure the compiler will do what's logical when presented with undefined behavior. ;)


Aggressive optimizations are boneheaded if they can be enabled by accident. Options that have the chance to break legitimate code should only be settable very explicitly.

Blzut3 said:

C++ doesn't get everything right, but most of the points I see here are pretty naive.



I don't think so. There's a vast difference between accessing undefined memory or an operation that can be implemented at the compiler maker's whim without ever putting up a warning sign.

Another sore spot for me is the wishy-washy-ness of defining sized integers. I think when someone requests a 16 bit value they have made up their mind that they do not want anything else for probably obvious reasons. An error would be better than silently expanding the variable in such a case if it can't be implemented.

Share this post


Link to post
Graf Zahl said:

Aggressive optimizations are boneheaded if they can be enabled by accident. Options that have the chance to break legitimate code should only be settable very explicitly.

If an optimization is breaking legitimate code then it's a bug. The example given earlier has no obvious solution. The unrolled solution Gez give actually changes the overload of << that would be called on the last iteration. I will also note that non-agressive optimization (-O1) does what I think people consider expected (negative value for last iteration).

Graf Zahl said:

I don't think so. There's a vast difference between accessing undefined memory or an operation that can be implemented at the compiler maker's whim without ever putting up a warning sign.

You ignored the part where I said these things often can't be determined during static analysis. If it could be, then the standard would claim these things shall make the program ill formed which means that a diagnostic is expected. This is why ubsan is a runtime thing. There are times where it can be determined, but the affected real code often is not of this variety.

There really isn't a vast difference between out of bounds memory access and any other undefined behavior in the standard. You're just very used to what that means because it gets hit all the time. However, all cases (or I should say nearly all just in case someone digs out some exception) of undefined behavior exists because specifying the behavior would require a performance hit. To put another way, if there was a easy answer for what should be done, it would be in the standard. The committee doesn't sit there going "where can we stick the phrase undefined behavior" it occurs because it was noted that interpretations could differ and no answer is obviously correct.

As for integer sizes. I do think it would be different if C++ were designed today without C compatibility. Probably would work more similar to Rust in that regard.

Share this post


Link to post
Graf Zahl said:

Actually, I know one exception, which is the 6502. Of course nobody would use C on such a system to begin with...

Oh, wow, I didn't realize that. I wrote a small bit of 6502 machine language on my Commodore 64, where it could record slowed-down audio through the joystick port! That processor was pretty weak, though the page 0 stuff was interesting.
I read that the Space Invaders arcade game, which used an 8080 (a precursor to the Z80) did not have shift instructions either, so, to move the sprites they added a hardware shifter to the Space Invaders motherboard. You wrote the value to an I/O port, then read it back shifted - heh!

Blzut3 said:

It is quite amusing reading this thread as someone who has been involved with building the C++ standard (C++ concepts in particular).

So, we can blame you for some of this? :)

Blzut3 said:

It seems like you all want undefined behavior to be removed except for the cases which you haven't had an issue with. What I mean by that is removing undefined behavior would imply that all array accesses need to be checked, but pretty much every C++ developer I know of would agree that doing that is stupid.

"You all?" heh. Personally, no, I want all possible undfined behaviors removed that I can get, and I kinda felt like that's what others were thinking too. And, I would love for there to be a compiler option to add array-checking code - what a time-saver that would be! My God. And, in non-performance code, I'd leave the option on, so, instead of a GPF and crash dump, I could write a log file, and, maybe even continue running long enough for someone to save their work. What it does now is stupid. Speed is useless if the app doesn't run.

Blzut3 said:

So what often happens when people complain about C++ is it really boils down to "they should fix what *I* think is stupid." Or even worse "all compilers should do exactly like *my* compiler."

I claim that "they" should have never built it that way to begin with. Why on Earth offer a command that has been half-ass defined? Signed-shift? Doing it right might affect performance on some platforms? So what? Nothing wrong with that platform's compiler throwing a warning: "Hey dummy, this command is slow on Platform X". Or allow the programmer to define the behavior. There's an idea.

Blzut3 said:

Now the C compatibility stuff in C++ is certainly debatable, but I'm sure C++ wouldn't be where it is today if it didn't have it. C compatibility is very much C++'s biggest mistake as much of the undefined behavior that people actually run into is a result of programming habits that derive from C. Not that it's defined in C either, but the C programmer notion of "knowing what something translates to in machine code" often gets people into traps like casting between incompatible types or abusing unions.

Hey, there's nothing wrong with compatibility. So, add a keyword for all the shiny new "C-squared" compilers that will cause old compilers to choke, but will enable new, 'defined' behavior. And, if it's silly to force a particular 'definition', give that power to the programmer. Instead of >>, provide assembly-like operators ROR, RLC, SLA, etc. And, yeah, it may generate a lot of code on some platforms, but, damnit, the language should cater to the programmer, not the other way around. I should not have to remember that "oh yeah, I'm using a signed number, everything goes to shit...". I expect the computer to work out those level of details. And, "integer overflow?" I consider that almost a misnomer. Integers don't overflow, they just count, and sometimes carry. It's like saying that my clock overflows every 12 hours.

Undefined memory accesses? If I allocate 4 bytes, and then read element 8, generate code to read from element 8. If I'm running an OS that doesn't appreciate that, let it crash. If not, it'll work (DOS). *OR* give me that compiler option to add runtime array checking, which would let me catch memory issues in DOS, and it would provide a nice direct error message. You could even bake the module, line number, and even array name into the error in a lot of cases. With this, I don't have to depend on the OS, or the debug build buffers to catch the over/underrun.

Blzut3 said:

As for optimizers, I'm always amused at the people who claim that "modern optimizers are great, there's no need for hand made ASM" and then turn around and claim that aggressive optimizations are "boneheaded." So basically the compiler should be all knowing and only aggressively optimize code in hot code paths which are written correctly (which is impossible to determine statically in real code). You're welcome to get the behavior you desire by turning the optimizer off. Then I'm sure the compiler will do what's logical when presented with undefined behavior. ;)

Ok, I have to call 'bullshit' on that one. No one claims that aggressive optimizations are boneheaded. Just about everyone would claim that erroneous optimizations are, in fact 'boneheaded'.

Undefined or not, if the optimization produces different output on vs. off, that's boneheaded.

And, no, I'm not naive, and, yes, I realize that the above statement is impractical to implement. But, you know why it is impractical? It's because of that word 'undefined'. Leaving something undefined, especially something that's next to trivial to fix, leaves these holes in the implementation that create the possibility for things to go wrong. Furthermore, an undefined behavior benefits no one - why defend it's right to exist?

In Entryway's example, the code as written by the programmer was well defined. Add 1000000000 to an integer. That has the same effect on a 32-bit integer, signed or unsigned. Yet, the compiler changed the code by defining a literal inside a for loop that was out of range for that for loop's variable. That is boneheaded.

Blzut3 said:

C++ doesn't get everything right, but most of the points I see here are pretty naive.

Man, that sorta sounds a bit snotty to me, I gotta say. Besides, 'points' can't be naive - 'points' don't think. Personifying 'points' is an undefined operation :P

Blzut3 said:

There really isn't a vast difference between out of bounds memory access and any other undefined behavior in the standard. You're just very used to what that means because it gets hit all the time.

Nope. Not even close, on either point.

Blzut3 said:

However, all cases (or I should say nearly all just in case someone digs out some exception) of undefined behavior exists because specifying the behavior would require a performance hit. To put another way, if there was a easy answer for what should be done, it would be in the standard. The committee doesn't sit there going "where can we stick the phrase undefined behavior" it occurs because it was noted that interpretations could differ and no answer is obviously correct.

So, not all platforms have numeric coprocessors, so don't provide multiplication, just let the poor programmer write their own implementation. Yeah, that makes sense?

Divide-by-zero is undefined. Shifting a signed integer is only undefined because the committee did not choose to define it. Performance hog or not, sometimes you need to do that shift! So the committee makes the programmer implement it manually. How could that scenario possibly improve performance?

The point of having a compiler (and a language for that matter) is to simplify the programmer's job. I'd let the compiler convert my number to a string of ones and zeros, if it would handle my signed shift properly! Again, if a particular operation is slow on a platform, the compiler can tell that to the programmer, and can even suggest alternatives. Instead, today it says "Hmmm. Might work, might not. Sometimes." C's claim to fame is in it's performance, but it caters to the lowest-common denominator. It's a philosophy thing: I want my language to be optimized for the best machines out there, but also have fall back solutions for older hardware. That's performance.

I have emulators for DOS, Atari, TRS-80, 8080, and more. I *know* that my computer can emulate the machine language of any one of those processors, including processor bugs, and, certainly each processors shifters. There's no reason C compilers cannot do the same. Hell, my TRS-80 could probably emulate a DOS app or two. It would suck, mind you, but it would run. And, then, I could take my fancy new Super-C compiler, and build me a version of that app for my 12-core 3.2 Mhz 64-bit box, and that app would scream. That's the mindset I'm looking for.

If the committee got on board that ship, Doom, for one, would rock.

Share this post


Link to post

I will add that Entryway's program is perfectly valid and fine if int were a 64-bit type!

fraggle said:

I don't see it as a problem: you define a new standard (eg. "C 2016"), eliminate as many of the ambiguities as possible and move on.


I do not see it as a problem either, but generally most old C code (all the way back to C89) should work on modern compilers. This fact is very important to those who standardize C. However an overzealous nature of the requirement of backwards compatibility can be nice in practice, it can do much harm (look at Microsoft's compatibility requirements on their own programmers).

fraggle said:

If your code doesn't work then it must have been relying on undefined behavior already - it's already broken.

The only people it really affects is people writing compilers, and that's fine - if they were targeting some weird system that really needed that undefined behavior (like a system that uses 1's complement arithmetic) they can just say that their compiler targets the older, more lax version of the standard, if they really need to.


And even then, there could be a #pragma (assuming it does not get destroyed by the proprocessor) which enables former undefined behavior for a newer safer standard. So you could still use everywhere else a safer version of C, but for that one spot where it is impossible because old behavior is relied upon, a #pragma enable broken_negative_shifts or similar.

fraggle said:

This is exactly the kind of stupid attitude that leads to insecure software.

People have this idea that it's fine to make any kind of dangerous, badly designed tool just as long as you find a way to blame the user when they hurt themselves. It's an irresponsible point of view and the people who defend it are actively promoting dangerous practices.


One of the primary reasons I program in Java instead of C (C when it is needed by JNI) is because of all the magic C has. I stopped really having to worry about printf destroying my stack when I use variadic arguments, filesystem layouts, etc. Going back to C I actually find it hard to write safe and portable code because there are so many traps to get stuck on, writing it actually takes longer than it does in Java.

But yes, I agree that it is bad to write insecure programs and just not care at all. It is sad to see that a death might be required to change a programmer's way.

fraggle said:

The C standard is full of examples: functions like strcpy(), sprintf(), etc. shouldn't exist. They're smart enough to have removed gets() now, but they should have removed those others as well. The only reason they haven't is that they can blame the user when those functions go wrong. But it's not a defence: the people with the power to remove those functions should be campaigning to remove them, not shifting the blame to the people hurt by the bad design they try to defend.


The last time I used those functions was a long time ago. There will be the common saying that as long as you pass a big enough buffer to sprintf, everything will be fine. Take for example:

char buf[9];
memset(buf, 0, sizeof(buf));
sprintf(buf, "%x", -42);
Looks all fine because -42 is passed to sprintf (as an int) which is printed as a 32-bit unsigned integer, which will never exceed 8 characters. However, 5 years down the line instead of int being 32-bits, it is 64-bit. Now suddenly you have a buffer overflow and one where a (malicious) user may be able to manipulate some stack entries. Much code which works fine now, will be broken in the future.

One way to probably write safe C code would to have a compiler just hate you. It would use a PRNG to figure out how large each integral type should be, what alignments are required, if a system is two/one's complement or uses a negative flag, big endian vs little endian, what happens when signed values are bitshifted. Throw in 24-bit or 56-bit ints too. Take all of these variables and randomly decide them on the next compilation of an entire program (or accept an argument which takes a seed). Would be very manual but you could see how all the undefined/unspecified behavior can break your program. It would lead to paranoia when programming, which is not a bad thing if used properly.

fraggle said:

Well, yes, but fundamentally I don't see why it ever needed to be part of the language standard. This is something that can be implemented as a custom library in an afternoon.


I will take the position that since C lacks operator overloading, using complex types would essentially turn into complexAdd(a, b). There are also structs, assignments, and casting. However with languages such as C++ or scientific ones which already have complex types, this is no issue.

Apart from that, I suppose the most logical position on a why is so that there is one standard for complex numbers than 42 different compilers doing it completely different from each other. Some of the new stuff in the standard these days is just taken from GCC. POSIX had int32_t, but it was __int32 (MSVC), int32_t (POSIX), Int32 (PalmOS), SInt32 (Mac OS), or DWORD (Win32), for quite awhile.

Share this post


Link to post
GhostlyDeath said:

Take for example:

char buf[9];
memset(buf, 0, sizeof(buf));
sprintf(buf, "%x", -42);
Looks all fine because -42 is passed to sprintf (as an int) which is printed as a 32-bit unsigned integer, which will never exceed 8 characters. However, 5 years down the line instead of int being 32-bits, it is 64-bit. Now suddenly you have a buffer overflow and one where a (malicious) user may be able to manipulate some stack entries. Much code which works fine now, will be broken in the future.


I have to say that this is my biggest gripe with C: The undefined size of integer types. If it has to be that way for performance, a compiler should at least have an option to define the size of an int, long or long long.

Of course it says a lot that current 64 bit systems still define int as 32 bit...

Share this post


Link to post
Graf Zahl said:

May I laugh? How are these languages supposed to get off the ground? There's no momentum. D is also suffering from the same problem.

The only language that got some push recently was Swift, and the sole reason for that is that many Apple developers are the same type of people as Apple users - looking up to their iGod and asking it for enlightenment.


Go has an enormous amount of traction, but it's domain-specific. There's tons and tons of Go code being written now for cloud computing. Docker, Kubernetes, etc. are all Go. There's stacks of internal google stuff written in Go. Somehow I've failed to actually write much myself, despite working in this space for a year now.

I hear things about Rust and Swift (and sometimes D) but I've never seen more than toys written in them, yet. Of course I wouldn't know whether a given iOS app was using Swift or not...

Graf Zahl said:

I have to say that this is my biggest gripe with C: The undefined size of integer types. If it has to be that way for performance, a compiler should at least have an option to define the size of an int, long or long long.


Go sort-of fixes this: it has defined integer sizes as types, but there's still an "int" which is defined as "at least 32 bit" so there's still room for mistake.

Share this post


Link to post
Jon said:

I hear things about Rust and Swift (and sometimes D) but I've never seen more than toys written in them, yet. Of course I wouldn't know whether a given iOS app was using Swift or not...


Swift got fairly good traction in the iOS developer community. But these people form an isolated group, nothing they write is even remotely portable - it has been like that with Objective-C already - if you want to do cross-platform stuff they are an utter nuisance to work with.

Which of course is all part of Apple's strategy: Condition even the developers to such a degree that they have no chance doing stuff on any other platform.

I think the gradual abandoning of standard APIs like OpenGL or ignoring Vulkan and replacing them with homegrown platform-locked solutions is also part of the same plan.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×