Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> (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 kind of insane.



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! :)


10 or 42?


Thank you. 42. I edited my comment above.


If array were, say, uint32_t* then what `*(array + i)` would do is actually `(intptr_t)array + i * 4` and not `(intptr_t)array + i`. If array were uint16_t* then it's `(intptr_t)array + i * 2`. In short the way the pointer arithmetic gets translated greatly depends on the type of the pointee and thus is not as primitive as it can be.


I think it depends on the coder if it makes more sense to them to write out the scale by element size or not. For me, the `+` is just pointer arithmetic and of course an addition in number of elements (so I don't think of the scaling at all).

Just saying that "actually it's just array + i" makes more sense - for me(!).


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.


They exist in the sense that their length is sometimes known if the full definition is visible. sizeof(array) isn't the same as sizeof(generic ptr to array).


This is not entirely a complete view. For example, sizeof() operator returns the declared size of an array. It seems that C recognizes the concept of an array.


"Because real programmers only need void*".


There's a language for that https://github.com/kyouko-taiga/void-lang


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


The OP probably doesn't want people to run away and never look back


Interesting. Looks like Forth. :-)


Speaking of such funny business: unfortunately I have seen *(array + i) quite a few times.

To make it worse, it’s in a parser for external data in binary format, where you really shouldn’t be playing funny tricks.


> 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.


Most programmers probably weren't aware of this, but no true Scottish C programmer would be unaware of it.

If you want to manipulate memory directly - which is risky though sometimes useful - C is one of the best languages in which to do it. Memory addresses are numbers, and C will let you work with those numbers in whatever way you want: add, subtract, multiply, divide... and if you didn't shudder at the suggestion of dividing a pointer because there are very, very few reasons to do so then C is not the language for you!

If you don't want to manipulate memory directly, you probably shouldn't be using C; stick with a nice garbage-collected, type-safe, object-oriented, cross-platform language. If you do want to manipulate memory directly, but you want more guarantees on what you can do with pointers, try Rust.


Just wanted to clarify that Rust allows for the same manipulation as C, it just all has to happen in the context of an unsafe code block. I think your comment might be taken to imply that Rust isn’t as capable as C in that regard.


Plenty of languages allow for the same memory handling capabilities, including BASIC.


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 <% and %>.


Wow, a new C trick I didn't know about. What's the history here?

Also, gross.


On some ancient systems { and } don't exist. So an alternarive made of more common characters is provided. There is also a two-character combination for [ and ] and a three character combination for |, & and some other characters. The feature is called digraphs/trigraphs and is disabled on most modern compilers by default.


> The feature is called digraphs/trigraphs and is disabled on most modern compilers by default.

Digraphs and trigraphs are treated differently. <% and %> are fine on GGC and Clang, but ??< and ??> are disabled by default. Both need the -trigraphs option.


Ah okay I knew about that whole thing, I didn't think C had so many of them.


I learned of this in college. Most C programmers know how C arrays work.


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


It's actually spelled out in K&R in more technical language:

"The array subscripting operation is defined so that E1[E2] is identical to *(E1+E2). Therefore, despite its asymmetrical appearance, subscripting is a commutative operation."

(my emphasis)


One of the very first IOCCC winners used the trick in 1984 (1984/anonymous):

    int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
    o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}


I think this is equivalent to:

    int i;
    int main(){
        for(
            ;
            i["]<i;++i){--i;}"]; // loop until i == 14? (the NUL byte on the end)
            read('-'-'-',i+++"hello, world!\n",'/'/'/') // read(0, <one byte of "hello, world!\n" at a time>, 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
    }


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).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: