Hacker News new | ask | show | jobs
by cookiengineer 756 days ago
Alternative headline should be "But I have been taught that using C++ makes me the better programmer" because the stereotypes of echo chambers on the internet raised a lot of unreflected programmers to be this way.

There is a place for C, where there's no alternative. But that place is where 99% of programmers never work, because they are not doing kernel nor firmware development (which, in the meantime, also has a lot of support by and for memory safe VMs and languages).

The issue I have with this narcisstic fatigue (similar to the author's point I assume) is that there is no reflection when they fuck up a codebase.

The best code is the code that is safe and easy to read, and doesn't need to use "clever tricks" that beginners cannot understand.

If you are using some tricks for type casting to implement your ideas into code, you probably should not write code.

Code should be dumb and easily maintainable. If it is not, you made the wrong choice for the programming language.

6 comments

Kernels have been developed in safer languages already before C became widespread outside Bell Labs, it is a myth that C is even required for that, other than historical baggage.
Especially when one adds how many "low level programming" idioms for C are, as far as I understand, undefined behaviour in C. Like assigning an address to then use as pointer to physical memory...

Which is extra visible when one looks at original UNIX sources and its many short assembly bits in separate files to handle bits of direct hw manipulation.

> Like assigning an address to then use as pointer to physical memory...

What do you mean by this? Like writing to a specific integer address?

    *((volatile unsigned *)(0x20001000)) = 0x12345678;
That's not UB and is also the only way to write to memmapped registers.
It's not UB. It's also not covered by standard - it's implementation-specified. What will happen is dependant on decision of compiler writers.

It is, however, not the only way to write to memmapped registers.

The original C way of doing so was to use an assembly function wrapping the actual act of reading/writing (honestly, better than doing the above, as it helps making it very explicit how the write will happen as well as abstracting any details like needing to add a barrier or whatsoever), the other way was to specify the symbol with address of the memmapped register in assembly, and link resulting object with C code.

A C implementation is, AFAIK, free to refuse the literal addresses used as pointers and pass as ISO C.

Both of the alternative do not generate same code though.

At least they don't without LTO.

The point of "implementation defined" is that the actual code generated is up to implementation, or in fact whether it even accepts such code.
> That's not UB

Please cite chapter and verse of the C standard which defines this behavior. Any edition.

I don't know if this particular example is UB or not, but the dichotomy here between 'defined' and 'not defined' is a false one, as C also specifies some constructs as having 'implementation defined' behavior. The behavior of such constructs is not defined in the standard, but is also not 'undefined' in the special sense of 'undefined behavior'.

Edit: Looks like the result in this case is implementation defined: https://stackoverflow.com/a/24212940

> There is a place for C, where there's no alternative.

Alternatives to C have started appearing for a long while, and they are quite mature now.

> because they are not doing kernel development

FluentBit, where this error occurred, is a userspace application.

> there is no reflection when they fuck up a codebase

C does not support runtime reflection, that is correct. It's one of the reasons why it's a programming and debugging nightmare.

> If you are using some tricks for type casting to implement your ideas into code

There seem to be no casting issues involved in https://www.tenable.com/security/research/tra-2024-17.

> The best code is the code that is safe and easy to read, and doesn't need to use "clever tricks" that beginners cannot understand.

It's just a pile of truisms.

Is your whole post even related to this article?

> Is your whole post even related to this article?

I think they were just reiterating TFA message in another way: Don't expose yourself to the pitfalls of C when the actual power of C is not really needed.

> There seem to be no casting issues involved in ...

Maybe go to http://osv.dev/list and search for OOB or Out of Boundary errors instead to make a study on whether this regularly happens or not? Especially when parsing arbitrary strings into a struct?

I'm not sure whether or not you got my message to prefer any programming language over C/C++ or whether you're trying to ignore that on purpose?

>But that place is where 99% of programmers never work, because they are not doing kernel development

Certain embedded targets are also legitimate use cases for C. For example, I am currently working on a project using an 8051 microcontroller. Aside from using assembly – which obviously isn't safe either – there is no practical alternative to using C. Rust may be making inroads for 32-bit targets, but will likely never be able to target an 8051.

> Code should be dumb and easily maintainable.

We now have enough resources to do this. When C was created, we had to do those tricks to have good performance. Currently C is best used for constrained devices, where sometimes you need those tricks.

> If it is not, you, yes ... you, made the wrong choice for the programming language.

Or your project manager or CTO, or some other stakeholder.

> Code should be dumb and easily maintainable.

Well ... that rules out everyone's pet favourite here to replace c.

which is C, because nothing can replace C.

C is portable assembly. Don't use it unless you really must.

Yes I know people want to replace C with Rust, no it won't work because Rust is not assembly, yes it can be used to replace anywhere between 90% to 99% of C and it shouldn't even be too hard. (Unless your embedded device manufacturer doesn't care, which of course they don't.)

> C is portable assembly.

Unfortunately, it isn't quite this, and many of the instances which people use it for this are actually undefined behavior; there's a hinterland of "things C compilers do" which are used as portable asm but are not part of C because they are UB.

I sometimes wonder if it would be feasible to define an actually portable typesafe macro assembler.

Only if you mean K&R C, where C was mostly a portable macro assembler, calling into Assembly code, written and compiled via an external assembler like in the first iteractions of the UNIX rewrite from Assembly into C.

The Lions book is a good example of what that C used to be like.

> The issue I have with this narcisstic fatigue (similar to the author's point I assume) is that there is no reflection when they fuck up a codebase.

Forgive my bluntness, but do you even know any C programmers? The ones I know are not at all a homogenous group (there isn't even anything resembling a "C community" - and IMHO that's a good thing btw).

> The best code is the code that is safe and easy to read.

These two things are not really related.

- Rust code is safe, but typically not easy to read

- C code is unsafe, but typically easy to read

...as a counter example though, C++ code is unsafe, and typically not easy to read.

Of course "readability" is entirely subjective too. Seasoned programmers are typically blind to the problems of their language of choice (and that includes C's sloppiness, and Rust's readability). Language beginners typically stumble over problems that experienced users of that language wouldn't even think of.

> Code should be dumb and easily maintainable.

Well, obviously, but see above, one programmers 'dumb and maintainable' is another programmers 'unmaintainable complicated mess'. IME it's harder to write code that's 'dumb and maintenable' in languages that nudge the programmers towards sugarcoating the program logic in high level abstractions than in "simple" languages like C, Go or Zig, because you don't just need to know the language, but also understand the high level abstractions the original author came up with (and which might have been a good fit at the start of the project, but not 3 years later when requirements have changed and the original programmers have long left for greener pastures).

IME it's not 'narcissism' that makes programmers pick C, but the incredible flexibility of the language, combined with being quite predictable what code will be generated (yes, optimizer passes do complex things, but after a while you'll develop an intuition for what the compiler will do, verifiable via godbolt).

Also for instance, look at the hoops other languages are willing to jump through just to get close - but never fully match - what's possible with simple text replacement in the C preprocessor (an incredible hot take, I know).

> C code is unsafe, but typically easy to read

Are you joking?

Absolutely not, modern C code (eg C99 or later) looks a lot different than C89. One problem is that the C stdlib is stuck in the C89 era, but at the same time most of the stdlib isn't all that important for writing C code).