Live data from Hacker News

The Descent to C (2013)

chiark.greenend.org.uk

31–40 of 96 posts

Re: The Descent to C (2013)

#31

great article, very informative and easy to read. I wish I would've known about this when I was learning C/C++ (around the time it was published no less), coming from higher-level languages like C# and PHP.

How is C# higher-level than C? I'm not aware of anything you can do in C that you can't do in C# as a first-class feature. Edit: I define the level of a language as the lowest-possible feature. I guess others define level as the highest-possible feature? I don't really know who's right here.

“...a high-level programming language is a programming language with strong abstraction from the details of the computer.”:

https://en.m.wikipedia.org/wiki/High-level_programming_langu...

Re: The Descent to C (2013)

#32
post #29

Earlier quoted context omitted.

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

what does this program do? i compiled it and ran it and got no output. i hope i haven't fork bombed myself or something

Prints "hello, world!", assuming sizeof(int) == sizeof(char*) (and the same alignments and ABI, etc).

Re: The Descent to C (2013)

#33
post #5

> (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…

Once the point is home, you can drive it a little bit more by exploiting the fact that string literals can be converted to pointers to the first characters, and do putc(2["ABCDEF"], stdout); This prints 'C'.

[deleted]

Re: The Descent to C (2013)

#34
post #5

> (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…

Once the point is home, you can drive it a little bit more by exploiting the fact that string literals can be converted to pointers to the first characters, and do putc(2["ABCDEF"], stdout); This prints 'C'.

[deleted]

Re: The Descent to C (2013)

#35
post #5

> (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…

Once the point is home, you can drive it a little bit more by exploiting the fact that string literals can be converted to pointers to the first characters, and do putc(2["ABCDEF"], stdout); This prints 'C'.

int const* const x; // C

int const& x; // C++

A reference is functionally equivalent to a const pointer. (Reference reassignment is disallowed. Likewise, you cannot reassign a const pointer. A const pointer is meant to keep its pointee [address].) The difference between them is that C++ const references also allow non-lvalue arguments (temporaries).

It is much easier to read from right to left when decoding types. Look for yourself:

- double (* const convert_to_deg)(double const x) // const pointer to function taking a const double and returning double

- int const (* ptr_to_arr)[42]; // pointer to array of 42 const ints

- int const * arr_of_ptrs[42]; // array of 42 pointers to const ints

- int fun_returning_array_of_ints()[42];

Try it out yourself: https://cdecl.org/

Hence, I am an "East conster". (Many people are "West consters" though.)

You can return function pointers:

typedef struct player_t player_t; // let it be opaque ;)

int game_strategy1(player_t const * const p)

{

    /* Eliminate player */

    return 666;
}

int game_strategy2(player_t const * const p)

{

    /* Follow player */
    
    return 007;
}

int (* const game_strategy(int const strategy_to_use))(player_t const * const p)

{

    if (strategy_to_use == 0)
        return &game_strategy1;

    return &game_strategy2;
}

Functional programming = immutable (const) values + pure functions (no side effects).

Consting for me is also a form of documentation/specification.

"East const" for life! :)

Re: The Descent to C (2013)

#36

> (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…

> nobody really does that, because that would be kind of insane Yeah. That's why people don't do things in C. It's more like most C programmers probably weren't aware of this. After your comment, we'll start to see C codebases everywhere with that.

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

Re: The Descent to C (2013)

#37
post #8

Some nitpicks with the article: 1. Much of section 2 is wrong except the part about arrays representing contiguous objects. The rest is largely an implementation detail. Zeta C and Vacietis work considerably differently, as allowed by the standard In addition there are many (mostly obsolete now) architectures in which, when you convert a pointer to an integer, you can't perform arithmetic and convert back because a p…

To be clear, signed integer overflow is undefined. Unsigned integer overflow is well defined.

This is why some C programmers dictate that all code must use signed integers to avoid unexpected bugs, but many others (including myself) disagree that's a good way of going about it since, as you said, it's not guaranteed to trap or do anything to help the programmer.

Re: The Descent to C (2013)

#38
post #37
post #8

Some nitpicks with the article: 1. Much of section 2 is wrong except the part about arrays representing contiguous objects. The rest is largely an implementation detail. Zeta C and Vacietis work considerably differently, as allowed by the standard In addition there are many (mostly obsolete now) architectures in which, when you convert a pointer to an integer, you can't perform arithmetic and convert back because a p…

To be clear, signed integer overflow is undefined. Unsigned integer overflow is well defined. This is why some C programmers dictate that all code must use signed integers to avoid unexpected bugs, but many others (including myself) disagree that's a good way of going about it since, as you said, it's not guaranteed to trap or do anything to help the programmer.

I've never seen signed-integers being required. They tend to introduce unexpected bugs rather than prevent it. Of course you can accidentally end up with signed integers when you didn't intend it. Here's my favorite accidental undefined signed integer overflow, assuming 32-bit integer size (the 64-bit version is similar)

  uint32_t foo(uint8_t x) { return x 
Yes, this is signed integer overflow, since x gets upgraded to a signed integer before the shift, if an integer is 32-bits in size, this can result in undefined behavior if the top bit of X is set. Fortunately I've never seen a compiler optimize this to stupidity.

Re: The Descent to C (2013)

#39
post #5

Earlier quoted context omitted.

Once the point is home, you can drive it a little bit more by exploiting the fact that string literals can be converted to pointers to the first characters, and do putc(2["ABCDEF"], stdout); This prints 'C'.

int const* const x; // C int const& x; // C++ A reference is functionally equivalent to a const pointer. (Reference reassignment is disallowed. Likewise, you cannot reassign a const pointer. A const pointer is meant to keep its pointee [address].) The difference between them is that C++ const references also allow non-lvalue arguments (temporaries). It is much easier to read from right to left when decoding types. Lo…

10 or 42?

Re: The Descent to C (2013)

#40
post #39

Earlier quoted context omitted.

int const* const x; // C int const& x; // C++ A reference is functionally equivalent to a const pointer. (Reference reassignment is disallowed. Likewise, you cannot reassign a const pointer. A const pointer is meant to keep its pointee [address].) The difference between them is that C++ const references also allow non-lvalue arguments (temporaries). It is much easier to read from right to left when decoding types. Lo…

10 or 42?

Thank you. 42. I edited my comment above.
Post reply on HN