Readit News logoReadit News
oconnor663 · 5 years ago
> The reason why I still like C is that it is a simple language. Simple in a sense that it is easy to express ideas and what to expect from it. For example, if you want to retrieve an array value with two offsets, one of which can be negative, in C you write arr[off1 + off2] while in Rust it would be arr[((off1 as isize) + off2) as usize].

There's an argument to be made that C is a simple language compared to some, I'll grant that. But implicit casts and the integer promotion rules really aren't a good example of simplicity. Terseness, sure, but not simplicity. For example, from https://www.cryptologie.net/article/418/integer-promotion-in...

    uint8_t start = -1;
    printf("%x\n", start); // prints 0xff
    uint64_t result = start << 24;
    printf("%llx\n", result); // should print 00000000ff000000, but will print ffffffffff000000
    result = (uint64_t)start << 24;
    printf("%llx\n", result); // prints 00000000ff000000
There are good reasons why e.g. Go, which prioritizes simplicity very highly, does not allow implicit integer casts.

pcwalton · 5 years ago
This is the difference between "simple" and "easy", as Rich Hickey so eloquently put it. C--this part of it, at least--is easy. You can write code without having to think about the different integer types, thanks to the complex integer promotion rules. Rust and Go are simple: the semantics are clear and understandable, at the cost of requiring the programmer to think about this aspect of numeric types. As programmers, we often mistakenly think that simple decisions automatically lead to an easy interface for the user; sometimes they do, but not always.

(On balance, I personally think that not having the implicit conversions, as Rust and Go do, is generally the better decision, but I certainly acknowledge that C is easier in this regard.)

pasabagi · 5 years ago
> (On balance, I personally think that not having the implicit conversions, as Rust and Go do, is generally the better decision, but I certainly acknowledge that C is easier in this regard.)

Really? I think there must be a better way - I also don't like implicit conversions, but the rust approach gets extremely verbose, especially if you want to check for truncation etc.

deeviant · 5 years ago
> Terseness, sure, but not simplicity.

I feel the same whenever somebody shows up talking about their favorite functional language. Sure it can be less code, but it looks like an explosion at the unicode factory.

Really though, I'd much rather have a few language "gotchas" that you need to learn once, over countless gotchas in other developers code because they are using a language the lends itself to ungrokable code. C really does have the properly where you can look at the code and just know what it does.

That said, I don't think C scales very well for big projects and I personally develop in C/C++/Python/ environment as a robotics system SWE. C for embedded stuff, C++ for nearly all system code, and python for the data/Deep learning stuff.

AstralStorm · 5 years ago
C does not have that property of not having gotchas whatsoever due to how undefined behavior is handled. (Or rather not handled) plus compiler and architecture defined behavior.

If you can spot undefined and other platform defined behavior always, someone probably would want to hire you as a security specialist.

mbrodersen · 5 years ago
C seems to scale very well for the Linux kernel?

Deleted Comment

beached_whale · 5 years ago
More so, the integer promotion rules of unsigned char and unsigned short are wrong and introduce potential signed integer overflow(in the case of unsigned short). If they were simple, they would have promoted to unsigned
queuebert · 5 years ago
Explicit casts are documentation. They may be tedious, but they are clear.
rramadass · 5 years ago
Right, this is why i never understood why people always harped on casts in C. Small price to pay for clarifying intent.
gameswithgo · 5 years ago
Yeah implicit casts are nice when working on a toy or personal project, but if you are putting code on a rocket or a game server or a self driving car etc, etc, absolutely not.
astrange · 5 years ago
This blog is by an ffmpeg developer and in my opinion you'd never have ffmpeg with that rule because everyone would have gotten bored of all the typing and stopped working on it.
ohazi · 5 years ago
But if the comparison is with C++, then they would both rank equally poorly here, because C++ doesn't want to behave differently.

If you write code like that in C frequently (e.g. working with system registers on embedded CPUs), you do eventually get used to explicitly casting defensively, and I agree that it's nice that you get saner defaults in e.g. Rust.

kazinator · 5 years ago
Since int is allowed to be as little as 16 bits wide, and start has promoted to int, start << 24 could be undefined behavior.

How it should work is that type of "start << 24" should be synthesized from the operands, without any idiotic "promotion" rule: it should therefore type uint8_t.

The shift should then require a diagnostic that it exceeds the width of the type. No undefined behavior nonsense; if the shift amount is a constant then it is statically diagnosable against the width of the type, and therefore should be.

This diagnostic will inform the programmer that their intent isn't being expressed.

astrange · 5 years ago
The type of "uint8_t << 1" being "uint8_t" would be confusing because it would always lose the top bits (unlike the current definition), but presumably the intent is to insert it into a larger bitfield or multiply by 2.

An alternative safer model called as-if-infinitely ranged integers solves this problem:

https://resources.sei.cmu.edu/library/asset-view.cfm?assetid...

flowerlad · 5 years ago
Explicit casts are problematic. Here's an example:

  int32 a;
  int16 b;
  //...
  b = (int16)a;
Later you decide to change b to int32.

  int32 a;
  int32 b;
  //...
  b = (int16)a;
Now you have a bug. If you didn't have that explicit cast the program would have worked as expected.

klodolph · 5 years ago
But, that's a good example of why implicit casts are bad!

      b = (int16)a;
This contains two casts... one explicit cast to int16, and one implicit cast back to int32. In languages without implicit casts, this would be an error.

oconnor663 · 5 years ago
I think the bug here is arguably also an implicit cast, from int16 back to int32. Languages that don't allow implicit casts at won't compile that last line.
aw1621107 · 5 years ago
I think it’s less a problem with explicit casts and more a problem of insufficient expressivity. If what you wanted was “cast to the type of b”, then something along the lines of C++’s decltype would be better:

    b = static_cast<decltype(b)>(a);
And as other commenters have pointed out, if implicit widening is disallowed that would also prevent the potential bug.

afr0ck · 5 years ago
If C lets you shoot yourself in the foot, it doesn't mean that you should do it! If you cast something then you need to take a moment to think about it first and check if it makes sense and whether it is really needed.
zabzonk · 5 years ago
> Now you have a bug.

Weeell, maybe not. I agree that whenever I see a cast, I think "WTF?", but maybe you want what the cast does?

LambdaComplex · 5 years ago
Wouldn't the compiler stop your second example? You're trying to assign an int16 value to b, which is now an int32.
gameswithgo · 5 years ago
the first program might not have worked as expected though.
josephcsible · 5 years ago
What's your justification for saying the second printf "should print 00000000ff000000"?
kelnos · 5 years ago
Not the parent, but my take on it is that you have only unsigned types in all of those signatures, but C goes and promotes the u8 to a signed quantity before doing the shift.

What I believe most people would expect is that 0xff would get shifted up, and that's it, but instead you end up with that plus a sign extension to fill out the "new" 32 bits at the top of the 64-bit value.

Dead Comment

Nginx487 · 5 years ago
I would say, it's rather primitive than just simple language. Lack of absolutely necessary features like generics, not to tell about memory and thread unsafety by design. There's zero sense to start a new project in C in 2021
bobbyi_settv · 5 years ago
Microsoft's compiler did not support C99 for a long time because they did not really think of it as a C compiler or a C/ C++ compiler; it is a C++ compiler. Its support for C90 was explained as being maintained "for historical reasons" ( https://herbsutter.com/2012/05/03/reader-qa-what-about-vc-an... ), but they had no reason to add support for new standards of a language that was outside the scope of the project.

It's odd for the author to complain about the commingling of C and C++ in compilers and then complain about Microsoft specifically not doing that.

flohofwoe · 5 years ago
The MSVC compiler actually has distinct C and C++ compilers (or at least compilation modes). In C++ mode, no C99 is accepted. What's different from clang and gcc is that the MSVC C++ compiler doesn't accept any "modern C" code, while clang and gcc have C++ language extensions for this (for instance clang accepts the full C99 designated initialization feature set in C++).
pjmlp · 5 years ago
I think they only moved away from that path due to customer pressure to keep supporting C on Windows.

That is the official excuse to only support C on Azure Sphere, despite the whole security sales pitch.

gattis · 5 years ago
I used to feel the same. For a long time I got away with using C and python for everything. They paired well and I always came away feeling that my code was expressed elegantly enough for me. C was for stuff that had to be fast, python for things that were more functional or meta. Then I started a project that needed to be faster than python but also required some heavy-ish meta-programming. I wrangled for way too long with the C preprocessor, and while way more powerful than I had imagined, still fell well short of being able to do what I needed without it looking and feeling like a dog that had to be put down. I think I would have needed a code-generating script to do it and to me that sounds disgusting. But C++ templates did exactly what I needed. They look awful and I still don't even understand my own code when I try to go back and read it. But it made me understand and appreciate that C++ does have a place, and for certain projects it might actually be the best choice. I'm sure some fan boys of the newer C++ descendents (rust/golang/zig/et al) will disagree.
Glavnokoman · 5 years ago
Type parametrization is actually done better in Zig. I wish it departed from "no implicit actions" dogma and let RAII in. It would be almost perfect...
erdeszt · 5 years ago
I kinda like that `defer` is very visible. Although I haven't worked on large Zig codebases so I don't know how that scales.
rramadass · 5 years ago
This is the usp of C++, it is a set of languages which can be combined as needed depending upon problem requirements (aka Multi-Paradigm). I personally always start with a subset and only add features as needed. Two old books which i found useful in thinking about how to use C++ are;

Scientific and Engineering C++ by Barton and Nackman.

Multi-paradigm design for C++ by James Coplien.

I have not as yet found any equivalents for "Modern C++".

LeCow · 5 years ago
I prefer Rust simply because it's safer and C++ has become monolithic. Also gotta love an excellent package manager.
vips7L · 5 years ago
The problem with Rust is that it still can't hit all of the targets that C++ can. I have to support IBM AIX and HP-UX and the only things that I know of that run there are C/C++ and Java.
Koshkin · 5 years ago
Well, all languages have good and bad parts. C and (the modern) C++ are two completely different languages in what matters most, and, to be fair, it is C++ in which much of the world-class software, like Unreal Engine, is written in. As much as I love C, if faced with a large project, I would only choose C++ as the implementation language.
munk-a · 5 years ago
At my first real dev job a big portion of my work heavily involved the Qt framework and, while at the time lambdas were still a big missing sore point, using that framework and their widespread habit of passing by const ref instead of pointer made me like C++ a lot more. I completely understand that pointers and dynamic memory management exists for really good reasons, but isolating global object creation into your main and leaning on internally managed lists of objects can be really strong from the perspective of writing good testable code.

I've been working primarily in PHP for a while now (and honestly put a lot less value on language comparison) but from what I've seen the expressiveness and power of C++ (at least in the ways I like it) have mostly been superseded by Rust and I think that'd be where I started if I ever need to work on something where in application logic would actually be a product bottleneck (instead of data source interaction).

jrsj · 5 years ago
Is Unreal Engine world-class software? I've heard a lot of negative things about it so it's hard to tell if it's just one of those things people complain about because its so popular or if it really does get things wrong
AstralStorm · 5 years ago
Bad things compared to what? Unity which does not have half the features out of the box and every other game implements basics like FPS limiter wrong?

Frostbite or Dawn Engine with its bad editors and performance problems?

Crytek used by few games? (This one has a chance to challenge UE) Unigine which is also rather rare?

offtop5 · 5 years ago
People tend to complain about game engines because they're just so accessible, yet allow you to do extremely complex things.

For example if I wanted to make a 3D platformer in unity, I could probably commission an artist to create the character, download a starter kit of some sort, and be done with it in about a week.

However this means I'm tying in so much code, and if anything doesn't work exactly the way I expect it I can complain. I might complain about the starter kit I purchased, I might complain about Unity crashing when the real reason for the crashes are my own crappy code.

Particularly when I was younger, I would often try to do way too much at once and this leads to frustration. But, theoretically you could create the next gears of war with three other people in about a year. Odds are you're going to run into tons of problems though just because it's so hard to do that

pjmlp · 5 years ago
Heck, most modern C compilers are written in C++ as well.
12thwonder · 5 years ago
Right, Browsers too are written in c++ for the most part
pretendgeneer · 5 years ago
I want to like C more, it's simpleness is great until you need to do anything at compiletime then the macro's blow any C++ complexity out of the water.

It's why I like zig so much, It really is the language that understands what was wrong with C and fix's those parts and doesn't do too much more.

Macros suck -> comptime is just zig code Pointer or array is ambiguous most of the time -> array and ptr notation Error handling things with a lot of possible errors leads to goto's or messy clean up -> errdefer and defer make that clean.

FpUser · 5 years ago
Browser times out on article.

Here is my take. I like C when I work on firmware for less powerful microcontrollers. I like modern C++ when I work on backend servers / middleware. I like Delphi / Lazarus when I work on desktop software. I like Java Script when writing web front-end libraries / apps. I like Python when shell script gets a bit too complex. Etc. Etc.

The real truth - I do not really like any of those. They're just practical tools that help me build my product. Product designed and created by me is what I like.

pjmlp · 5 years ago
> It is not safe e.g. out of bounds array access is rather common and there’s no runtime check for that while e.g. Borland Pascal let alone something more modern had it (even if you could turn it off in compilation options for better performance

ALGOL dialects for systems programming had it 10 years before C was born.

CPL the language that BCPL subset was designed as bootstrapping compiler, had it.

PL/I the language used on Multics had it.

PL.8, language created by IBM for their RISC project and LLVM like compiler toolchain, had it.

As did plenty of others.

> And in most cases you know what will compiler produce—what would be memory representation of the object and how you can reinterpret it differently (I blame C++ for making it harder in newer C standard editions but I’ll talk about it later), what happens on the function calls and such. C is called portable assembly language for a reason, and I like it because of that reason.

When the target CPU is an old 8 or 16 bits CPU, and the code gets compiled with optimizations turned off.