Live data from Hacker News

Essential C (2003) [pdf]

cslibrary.stanford.edu

61–70 of 84 posts

Re: Essential C (2003) [pdf]

#62

Earlier quoted context omitted.

I don't see how this proves the point. For someone who doesn't have the operator precedence rules memorized, it isn't clear whether the above code means this: x = *stack; stack--; or this: stack--; x = *stack; Combining those two operations into one line is a trade-off I will never agree with. And I'm a fan of C myself: https://gist.github.com/cellularmitosis/3327379b151445c602ad... https://gist.github.com/cellularmi…

> The one-liner and two-liner compile to the same number of instructions, so I don't see how either "avoids inconsistent state". It's about performance, or thread safety, or anything like that; it's about having a coherent mental model of the code. A statement should, if possible, represent a single, complete operation. Invariants should not be violated by a statement, with respect to its environment. (This more true…

> It's not operator precedence—that's a separate issue

> It's even mnemonic—when the increment symbol goes before the thing being incremented, the increment happens first; else after—but even if not, it's a fairly basic language feature.

I think you missed the issue.

This is 100% about operator precedence, and has nothing to do with the decrement operator being in front of or behind the variable.

This expression:

    *stack--
means either this:

    (*stack)--
or this:

    *(stack--)
depending on the operator precedence rules.

If this is the layout of memory:

             ~~~~~~
    stack-1: | 52 |
    stack:   | 23 |
    stack+1: | 19 |
             ~~~~~~
(* stack)-- evaluates to 22, while *(stack--) evaluates to 52.

https://godbolt.org/z/P7Ghfc

Re: Essential C (2003) [pdf]

#63
post #54

Earlier quoted context omitted.

Yep so if we're going with style I'm very happy with the functions dashed off there. Nobody will confuse those even when very, very tired (similar effect on the brain to being drunk). There is zero difference in the generated output. Calling those functions tells you exactly what they are and what they do. Vertical space is not an issue at all with 3 line functions. Relying on post-increment? Make sure it's a one lin…

> Make sure it's a one line block that is totally unbraced with only single letter variable names if you do it because otherwise it's just faux-macho C and that's /weak/. I think you're projecting. The point being made was that when you're writing a simple stack (as you often might do in C, since the standard library and the language itself conspire against providing you one) and you don't have the overhead to write…

Sorry no. That's not for you in particular that's just a general comment on macho C, which I think we've all seen.

    int abc(int a, int b, int c)
    {

    }
I can do postincrement. I learned C the macho way. We all still have to read that crap. Now I know better when I'm writing it. I strongly disagree that

   a = *stack--;
   *++stack = b
is better in any way beyond "I'm a macho C guy" than

    a = pop_int();
    push_int(b);
https://en.wikipedia.org/wiki/Duff%27s_device

It's fun when you first see it. Sure.

Re: Essential C (2003) [pdf]

#64

Earlier quoted context omitted.

> The one-liner and two-liner compile to the same number of instructions, so I don't see how either "avoids inconsistent state". It's about performance, or thread safety, or anything like that; it's about having a coherent mental model of the code. A statement should, if possible, represent a single, complete operation. Invariants should not be violated by a statement, with respect to its environment. (This more true…

> It's not operator precedence—that's a separate issue > It's even mnemonic—when the increment symbol goes before the thing being incremented, the increment happens first; else after—but even if not, it's a fairly basic language feature. I think you missed the issue. This is 100% about operator precedence, and has nothing to do with the decrement operator being in front of or behind the variable. This expression: *st…

> operator precedence

Right, yes. I got confused by your example, because the example is definitely about pre- vs post-increment. My point about idioms still stands, though.

> (* stack)-- evaluates to 22, while * (stack--) evaluates to 52.

Actually, (* stack)-- evaluates to 23, but changes *stack to 22 :)

Re: Essential C (2003) [pdf]

#65

Earlier quoted context omitted.

Yes. ssize_t is signed.

ssize_t should never be used. It's not guaranteed to have a full negative range, only to be able to represent -1. Use ptrdiff_t as a signed size type.

Out of curiosity, do you know of any implementations where ssize_t has that kind of range limitation?

Re: Essential C (2003) [pdf]

#66

Earlier quoted context omitted.

> It's not operator precedence—that's a separate issue > It's even mnemonic—when the increment symbol goes before the thing being incremented, the increment happens first; else after—but even if not, it's a fairly basic language feature. I think you missed the issue. This is 100% about operator precedence, and has nothing to do with the decrement operator being in front of or behind the variable. This expression: *st…

> operator precedence Right, yes. I got confused by your example, because the example is definitely about pre- vs post-increment. My point about idioms still stands, though. > (* stack)-- evaluates to 22, while * (stack--) evaluates to 52. Actually, (* stack)-- evaluates to 23, but changes *stack to 22 :)

> Actually, (* stack)-- evaluates to 23, but changes *stack to 22 :)

Thanks, I just realized I had that wrong :) https://pastebin.com/n7sHzW3p

Re: Essential C (2003) [pdf]

#67

Earlier quoted context omitted.

ssize_t should never be used. It's not guaranteed to have a full negative range, only to be able to represent -1. Use ptrdiff_t as a signed size type.

Out of curiosity, do you know of any implementations where ssize_t has that kind of range limitation?

Nope. But I do know of at least one implementation where it's not present at all—msvcrt. ssize_t isn't specified in the c standard, it's part of posix. ptrdiff_t is standard.

Re: Essential C (2003) [pdf]

#68

This guide is short (which is always nice) but not has a couple of flaws in places in the brief skim I gave it. For example: > In particular, if you are designing a function that will be implemented on several different machines, it is a good idea to use typedefs to set up types like Int32 for 32 bit int and Int16 for 16 bit int. Use please > The char constant 'A' is really just a synonym for the ordinary integer val…

It took Microsoft 16 years to support most of C99 in MSVC, and they are still not completely done after 21 years. I think for a document last updated in 2003 it's ok not being based on C99 ;)

Re: Essential C (2003) [pdf]

#69
post #18
post #9

This is a pretty neat guide if you’re cheap and have moral qualms about pirating K&R. Still, I think the best introduction to C remains K&R.

K&R is woefully out of date. Gives you no info on how to do things safely and sanely. And encourages a leet style of programming that results in catastrophic edge case bugs. As you can see in comments above where naive code that iterates backwards through an array fails when the array size is 0. Worse K&R leet style buys you absolutely nothing with a optimizing compiler written in the last 30 years.

Eh, K&R Second Edition is a very useful book also today. The only downside is that stops at C89 and hasn't been updated for C99.

Re: Essential C (2003) [pdf]

#70

I don't want to learn C but certain types of applications there's really no practical alternative.

I typically see Rust marketed as a better replacement for C. In what cases isn't it a practical replacement?

Rust is a fine replacement for C++, but not really for C, and the reasons why Rust can't be a replacement for C are very similar to why C++ can't be a replacement for C. Everybody who chooses C today has slightly different reasons to do so, but one important reason is that C is a very small language with a very small standard library, and most parts of the standard library can be ignored without losing any of the "qualities" of the C language. IME a small language feature set makes it easier to evaluate and integrate third party code, it's hard to say exactly why that is, but that's my experience anyway. Of course one could say "nobody forces you to use the whole feature set of Rust", but the same has been said in the C++ world for decades. The problem is that everybody selects their own subset of the language when having the choice.
Post reply on HN