Live data from Hacker News

“C is how the computer works” is a dangerous mindset for C programmers

words.steveklabnik.com

341–350 of 387 posts

Re: “C is how the computer works” is a dangerous mindset for C programmers

#341
post #297
post #251

Earlier quoted context omitted.

> most other answers will raise an eyebrow from me Portability. A C library can be trivially linked with any other language. But one will be hard pressed using a python library form Ruby, for example.

This is literally the reason I most recently wrote some C, but - do you really need linking ? You can pretty easily use a Python library from Ruby by writing a little loop in Python that accepts JSON input and produces JSON output, and calling that as a Ruby subprocess. It's fairly rare that you actually need to be in the same process. (The thing I wrote was a wrapper for unshare(), so it did strictly need to be in-p…

> You can pretty easily use a Python library from Ruby by writing a little loop in Python that accepts JSON input and produces JSON output, and calling that as a Ruby subprocess.

If you're a Ruby application, sure, you can use a Python library by forking off a subprocess.

Just make sure to document the installation requirements - in addition to having Ruby and the correct gems installed, you also need Python (which version?) and the correct pip libraries installed.

If you're a Ruby library, instead of an application, forking off a Python process is a nonstarter, unless you want to propagate those requirements out to every single application using your library.

Re: “C is how the computer works” is a dangerous mindset for C programmers

#342
post #310

Earlier quoted context omitted.

Probably talking about CLion, their C/C++ offering.

Yes, CLion, my mistake, sorry.

Hmm, that's a bit disappointing…might I suggest trying out a LibClang-based IDE, if CLion isn't using it already? It can do wonders even to "stupid" applications. For example, my Sublime Text (by itself, with only basic knowledge of C++ keywords and such) with clangd tells me that std::make_shared comes from this code in :

  template
  inline _LIBCPP_INLINE_VISIBILITY
  typename enable_if
  ::value,
      shared_ptr
  >::type
  make_shared(_Args&& ...__args)
  {
      return shared_ptr::make_shared(_VSTD::forward(__args)...);
  }

Re: “C is how the computer works” is a dangerous mindset for C programmers

#343

My favorite take on the severity of the UB problem, https://blog.regehr.org/archives/1520 : > Tools like [Valgrind] are exceptionally useful and they have helped us progress from a world where almost every nontrivial C and C++ program executed a continuous stream of UB to a world where quite a few important programs seem to be largely UB-free in their most common configurations and use cases...Be knowledgeable about…

> Be knowledgeable about what’s actually in the C and C++ standards ... turns out to be an extremely tall order; the 2017-11-17 working draft of the C++ standard is 1,448 pages in PDF format. At some point, I wonder when programmers who are required to care about correctness throw up their hands and say "This language isn't reasonably human-sized for a user to know they're using it correctly." At which point the imme…

  Why don't you use something else?
Because, most (all?) of those other languages fall into one of three traps, they also have a lot of undefined behaviors, they define the behaviors in ways that don't reduce actual programming bugs (javascript!), or they have so rigorously defined the language with underlying assumptions (say a really strong memory model, signaling NaN, overflow exceptions, etc) that the performance is sub-optimal on any platform that doesn't exactly fit the expected machine description.

Really, C isn't hard if you burn your copy of K&R and stop trying to be so damn clever. It also turns out to be a lot more readable, if a bit more verbose, if one ignores a lot of its "features" and pretends its pascal with a single statement, without side effects per line. That includes using pointers in any kind of arithmetic (or type casting), instead using them only as though they were C++ references. (and a few other basic rules).

So, while I don't think rust is a particularly good language, I'm also starting to think that everyone should be forced to use it early in their career so they are forced to consider object ownership and lifetime in a rigorous way. Then when they move to C/C++/etc they wont be foot-gunning themselves at every turn.

Re: “C is how the computer works” is a dangerous mindset for C programmers

#344
post #332

Earlier quoted context omitted.

What makes you sure of this? I'm reasonably familiar with modern C, but I don't feel confident of the answer here. A search of Stackoverflow doesn't bring up anything that seems authoritative for C. The most relevant quotation I can find is in the Rational for C99, where Section 6.3.2.3 has: Implicit in the Standard is the notion of invalid pointers. In discussing pointers, the Standard typically refers to “a pointer…

I found this: https://stackoverflow.com/questions/51083356/does-the-c-stan... What if it's not an "invalid pointer", but a pointer to a memory-mapped IO address, ROM, etc? I grew up learning C on 16-bit machines in the early 90's. Hard coded pointer values were very, very common.

Good find! I don't think there is anything "authoritative" there, but the discussion seems high quality. My take is that a lot of smart people disagree on which parts of that example are implementation-defined, implementation-undefined(!), or undefined-behavior. Most (but not all) think that the initial assignment is implementation defined, but 'davislor' suggests in his answer that "the line void * ptr = (char * )0x01; is already potentially undefined behavior, on an implementation where (char* )0x01 or (void* )(char* )0x01 is a trap representation".

> What if it's not an "invalid pointer", but a pointer to a memory-mapped IO address, ROM, etc?

Yes, this is central to the question. And how is the compiler to know? Is it safe to presume that the compiler can't know, and thus can't presume undefined behavior? I think the answer is in the comments you linked where 'supercat' replies to 'Peter Cordes':

The Standard makes no attempt to mandate that all implementations be suitable for low-level systems programming, nor does it in any way imply that it's possible to have a quality implementation that is suitable for low-level or systems programming without it supporting behaviors beyond those mandated by the Standard (and which might not be processed predictably by implementations that aren't suitable for systems programming).

Which is to say, yes, for a compiler implementation to actually be useful for low-level programming, it must behave in a predictable manner when given literal addresses. Unfortunately, it may be possible for a C compiler to be "standards conforming" without actually being useful for this purpose. One can only hope that at least some compilers will continue to "do the right thing" despite that lack of explicit requirements.

Re: “C is how the computer works” is a dangerous mindset for C programmers

#345

One of the blog posts I keep meaning to write is in the vein of "Why C is not portable assembly." Ironically, most of my points about C failures aren't related to UB at all, but rather the fact that C's internal model doesn't comport well to important details about machines: * There's no distinction between registers and memory in C. A function parameter that is "register volatile _Atomic int" is completely legal and…

Don't forget arithmetic operations that can detect various corner cases. Writing something like: sum = x + y; if(sum And then hoping the compiler will optimize your if statment to a single overflow CF check is a bit silly.

If you just use `sum < x` or `sum < y` rather than both, C compilers do reliably perform that optimization. (You don't need both; `sum < x` and `sum < y` are always both false or both true.)

Re: “C is how the computer works” is a dangerous mindset for C programmers

#346
post #268

Earlier quoted context omitted.

A good optimizing compiler will just elide the whole code block.

Only if there was UB, and the point is that there probably isn't. (I'm not really knowledgeable on the C standard, so I might have misinterpreted something.)

Indeed, but in fact there is UB:

https://stackoverflow.com/questions/11962457/why-is-using-an...

Re: “C is how the computer works” is a dangerous mindset for C programmers

#347

Earlier quoted context omitted.

LLVM has 2.5 million lines of code, but I think it also depends upon the number of templated methods etc, not just LOC. I'm surprised you haven't seen this point made many times. It would be a full-time job on the internet asking people why they have code completion issues with C++ IDEs.

No, I know what you're talking about; this is an issue in general if you'd not using tooling that's aware of the ins and outs of C++. The point is that the IDEs I have used essentially run the compiler on the file, so they can actually understand what the templates are doing and get through them.

Templates can inherently not be understood. It's duck typing 3 degrees of freedom with 3 unknowns. You need concepts which adds 1 known into the equation, (the final missing degree of freedom is the dreaded ctrl-click-into-an-interface-instead-of-the-concrete-class which is hard to avoid). Tell me one IDE that will give any meaningful information about buzz in the example below:

    template
    void foo(T bar) { bar.buzz(); }
Other than that, finding foo is usually easy, i've had good experience with qtcreator or clangd, you just have to be very very sure that your include paths are correct, qtcreator is good in that it uses CMakeLists as project-files so all this is done automaticaly.

Re: “C is how the computer works” is a dangerous mindset for C programmers

#348
post #344

Earlier quoted context omitted.

I found this: https://stackoverflow.com/questions/51083356/does-the-c-stan... What if it's not an "invalid pointer", but a pointer to a memory-mapped IO address, ROM, etc? I grew up learning C on 16-bit machines in the early 90's. Hard coded pointer values were very, very common.

Good find! I don't think there is anything "authoritative" there, but the discussion seems high quality. My take is that a lot of smart people disagree on which parts of that example are implementation-defined, implementation-undefined(!), or undefined-behavior. Most (but not all) think that the initial assignment is implementation defined, but 'davislor' suggests in his answer that "the line void * ptr = (char * )0x…

> Unfortunately, it may be possible for a C compiler to be "standards conforming" without actually being useful for this purpose.

It is possible for a C implementation to be conforming witout supporting low-level programming. Sometimes it even makes sense, like if you're running C code on GraalVM's LLVM bitcode interpreter. But there is no trend of "standard" C implementations following this route. While modern C compilers like to aggressively exploit undefined behavior, they generally make reasonable decisions for implementation-defined behavior. In this case, all major compilers will compile

    *(int*)0x12345678
to the obvious assembly, and what happens then depends on what your memory map looks like.

(Caveat: the compiler will still perform normal optimizations like removing unused loads or redundant stores. If the address actually points to memory-mapped I/O, you need `volatile` to prevent that.)

Re: “C is how the computer works” is a dangerous mindset for C programmers

#349
Assembly is how the computer works. Wait no - Binary is how the computer works. Wait no, NAND gates and ALUs are how the computer works. Every level of abstraction is just that. A layer of abstraction. You can pick any layer, then go down and say "learn that".

Re: “C is how the computer works” is a dangerous mindset for C programmers

#350

Earlier quoted context omitted.

It would also be nice if Rust had a specification. Without a spec it's impossible to build a correct alternative Rust implementation.

That's not true, as mrustc proves. The C and C++ specs are incredibly ambiguous and frankly I don't know if they add that much value over good documentation.

When it comes to high-level stuff especially in C++, like the exact rules for template deduction, I think the specs are reasonably clear and do add value. But for the low-level memory model and undefined behavior, the specs are indeed extremely ambiguous. In fact, they're often outright 'wrong', in the sense that they have too little undefined behavior to support common compiler optimizations (e.g. [1] [2])... while making other things undefined for no benefit [3].

[1] https://fzn.fr/readings/c11comp.pdf

[2] https://www.cs.utah.edu/~regehr/oopsla18.pdf

[3] https://www.imperialviolet.org/2016/06/26/nonnull.html

Post reply on HN