Live data from Hacker News

The Descent to C (2013)

chiark.greenend.org.uk

51–60 of 96 posts

Re: The Descent to C (2013)

#51
It's not inherent in C being a low-level language that it has such a painful memory model. It's a consequence of having to fit the compiler into a really tiny machine by modern standards. Originally, there were no function prototypes.

I once proposed a backwards-compatible way out for C.[1] Basically, you get to talk about arrays as objects with a length, even if that length is in some other variable. And you get slices and references. It was discussed enough to establish that it could work, but the effort to push it forward wasn't worth it.

Slices are important. They let you do most of the things people do with pointer arithmetic. Once you have size and slices, you can still operate near the memory address level.

[1] http://www.animats.com/papers/languages/safearraysforc43.pdf

Re: The Descent to C (2013)

#52
post #3

Hey! The author wrote PuTTY! That was a very informative read. I feel like I am the exact target audience: I'm coming from a programing background steeped in C# and I'm learning C from the K&R book.

Never thought pointers could expire, but it makes sense.

That's a fairly common trap for new C coders. Setting a pointer to something on the stack and then returning that pointer when you exit the function. It's extra insidious because it will appear to be working fine until you call another function and then try to dereference the pointer. Then your data suddenly becomes corrupt even though your program was nowhere near it.

Re: The Descent to C (2013)

#53

> (In fact, that's what array[i] means – the language defines it to be a synonym for *(array+i).) To really drive home the primitiveness of C arrays, should probably also mention that, because addition is commutative, you could also write i[array] and somewhat surprisingly, it will compile, and work, and it means "*(i + array)" which is equivalent to "*(array + i)" But nobody really does that, because that would be k…

This sub-thread is better than a Vim thread. :)

Re: The Descent to C (2013)

#54

Earlier quoted context omitted.

This is (sort of) the idea behind Rust's lifetimes. (Rust focuses more on scope, whereas pointer expiry is more about “object lifetimes”, but it's basically the same thing.)

I don't think that's true, it's just that the lifetime of a stack object is limited by its scope. Clearly when it goes out of scope its lifetime needs to end. You can end that lifetime prematurely, for example if you drop(x) then the lifetime of x ends immediately [ in fact the implementation of drop is entirely empty, but it takes the actual x itself as a parameter, not a reference to it, and so when the empty funct…

Rust used to use lexical scope, but now uses scope based on the control flow graph.

Re: The Descent to C (2013)

#55

> (In fact, that's what array[i] means – the language defines it to be a synonym for *(array+i).) To really drive home the primitiveness of C arrays, should probably also mention that, because addition is commutative, you could also write i[array] and somewhat surprisingly, it will compile, and work, and it means "*(i + array)" which is equivalent to "*(array + i)" But nobody really does that, because that would be k…

This is actually a really good way to drive home that arrays don't really "exists" in C and are just syntaxic sugar for pointer arithmetic.

Re: The Descent to C (2013)

#56
post #50
post #36

Earlier quoted context omitted.

Eh? Says who? Every C programmer I've ever met knows about this. It's basic C.

Yeah you have to do something like this if you want to truly raise eyebrows. /\ */ best c comment *\ /

One trick that I like is replacing { and } with .

Re: The Descent to C (2013)

#57
post #45

Earlier quoted context omitted.

As per the somewhat famous blog post: C is not a low-level language. Especially on todays CPU’s I fail to see why would we consider C anything close to truly low level. It has no real way of managing cache, has absolutely zero support for vector instructions, etc. * With these in mind, Rust is lower and higher level than C at the same time. * other than some compiler specific pragmas, but I would be hesitant to call…

> Especially on todays CPU’s I fail to see why would we consider C anything close to truly low level. It's effectively the lowest you can go without throwing portability out the window. It doesn't let you manage caches directly, but it gives you good control over memory layout in general, and that's often enough to give you good cache usage across a variety of chips. If you want to go lower than that, you're probably…

It's a poor fit for this role, even if it is in practice what we have available today.

One of my favourite examples is volatile. Volatile is more crazy in C++ but it's pretty crazy even in C. In both cases the standard basically shrugs, "Hope you know what you're doing because we sure don't" and offers no real insight into what this feature promises to do for you. But there is no other mechanism provided for MMIO.

Think of Rust's std::ptr::read_volatile (and write_volatile). These intrinsics do the thing you actually wanted (reading, or writing, a fixed size blob of "memory" that presumably wasn't really just RAM) and thus are important for writing device drivers and so on with MMIO.

[ You may be thinking, "But I need the correct size of blob read or written or my driver won't work", Rust has generics, so these functions are generic over the integer type you're reading/ writing, if you read a u64 that's a 64-bit read, if you write four u8s that 4 x 8-bit writes and so on ]

But C's volatile is a type qualifier instead. Why? Would it mean anything to, for example, integer divide an MMIO fetch by fifteen and write it back? a/= 15; No. So then why make it a type qualifier? When volatile was added to C they'd only just invented simple optimisations like re-ordering so they had no idea this was a bad idea, and it seemed simpler than adding an intrinsic (though not by much) but today we know better.

Re: The Descent to C (2013)

#58

Earlier quoted context omitted.

This is (sort of) the idea behind Rust's lifetimes. (Rust focuses more on scope, whereas pointer expiry is more about “object lifetimes”, but it's basically the same thing.)

I don't think that's true, it's just that the lifetime of a stack object is limited by its scope. Clearly when it goes out of scope its lifetime needs to end. You can end that lifetime prematurely, for example if you drop(x) then the lifetime of x ends immediately [ in fact the implementation of drop is entirely empty, but it takes the actual x itself as a parameter, not a reference to it, and so when the empty funct…

Scope and lifetime are two different things, but they're related in the case of automatic (local) variables.

Scope is the region of program text in which an identifier is visible.

Lifetime is the duration during program execution in which an object (stored in memory) exists.

If you define a local variable, the scope of its identifier extends from its declaration to the end of the enclosing block; its lifetime is the execution of the enclosing block.

If you allocate an object on the heap by calling `malloc()`, its lifetime ends when its deallocated by calling `free()`.

An object defined at file scope or with the `static` keyword has a lifetime that's the entire execution of the program.

In either case, if you have a pointer to the object, that pointer becomes invalid when the object it points to reaches the end of its lifetime. (And C doesn't make it particularly difficult to cause problems by trying to access an object that no longer exists.)

Re: The Descent to C (2013)

#59
Surprise: there's a whole bunch of different reality under your the convenient illusions of your "high level" programming language.

Hardware is the reality. It's not very much like the Java or Python programming model. We shouldn't hide this from programmers.

Re: The Descent to C (2013)

#60

Earlier quoted context omitted.

This “trick” has been know at least as far back as 2008: https://stackoverflow.com/questions/381542/with-arrays-why-i...

One of the very first IOCCC winners used the trick in 1984 (1984/anonymous): int i;main(){for(;i["]

I think this is equivalent to:

    int i;
    int main(){
        for(
            ;
            i["], 1)
        ) {};
    }

    int read(j,i,p){
         write(j/p+p,i---j,i/i); // write(0/1+1, i-- - 0, 1) --> write(1, i, 1) --> write a byte to STDOUT
    }
Post reply on HN