Live data from Hacker News

Some Obscure C Features

multun.net

31–40 of 147 posts

Re: Some Obscure C Features

#31
post #26

> a[b] is literally equivalent to *(a + b). You can thus write some absolute madness such as 41[yourarray + 1]. Wow. This has to be the best C obscurity that I've ever seen.

As an embedded dev, I didn't know these things were actually obscure. Granted, we never used such things in production.

Re: Some Obscure C Features

#32
post #13

The preprocessor trick of passing function macros as parameters is not that obscure. I have seen it used and I've used it myself. It is very useful when you have a list of static "things" that you need to operate on. Say I have a static list of names and I would like to declare some struct type for each name. I also would like to create variables of these structs at some point, and I would always do so for the entire…

I would call that the X macro pattern, but the wiki article doesn't agree that it should pass the `fn` as the argument. Not sure if that's important.. https://en.wikipedia.org/wiki/X_Macro

Re: Some Obscure C Features

#33
Most of these are due to the cruft added in C99 and later.

Compile-time trees are possible without compound literals.

More than twenty years ago, I made a hyper-linked help screen system a GUI app whose content was all statically declared C structures with pointers to each other.

At file scope, you can make circular structures, thanks to tentative definitions, which can forward-declare the existence of a name, whose initializer can be given later.

Here is a compile-time circular list

   struct foo { struct foo *prev, *next; };

   struct foo n1, n2; /* C90 "tentative definition" */

   struct foo circ_head = { &n2, &n1 };

   struct foo n1 = { &circ_head, &n2 };

   struct foo n2 = { &n2, &circ_head };
You can't do this purely declaratively in a block scope, because the tentative definition mechanism is lacking.

About macros used for include headers, those can be evil. A few years ago I had this:

   #include ALLOCA_H  /* config system decides header name */
Broke on Musl. Why? ALLOCA_H expanded to . But unlike a hard-coded #include , this is just a token sequence that is itself scanned for more macro replacements: it consists of the tokens {}. The on Musl defines an alloca macro (an object-like one, not function-like such as #define alloca __builtin_alloca), and that got replaced inside , resulting in a garbage header name.

Re: Some Obscure C Features

#34
A nice but I guess more of a linker feature is if you declare a function as __weak__, you can check it at runtime for == NULL to determine if the application was built with the function defined.

Re: Some Obscure C Features

#36
post #8

sizeof actually doesn't evaluate its expression for side effects most of the time; only if the operand is a variable-length array is it evaluated.

And it's not even entirely clear what that means.

The standard says:

"If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant."

But what does it mean to evaluate the operand?

If the operand is the name of an object:

    int vla[n];
    sizeof n;
what does it mean to evaluate `n`? Logically, evaluating it should access the values of its elements (since there's no array-to-pointer conversion in this context), but that's obviously not what was intended.

And what about this:

   sizeof (int[n])
What does it mean to "evaluate" a type name?

It's not much of a problem in practice, but it's difficult to come up with a consistent interpretation of the wording in the standard.

Re: Some Obscure C Features

#37
post #34

A nice but I guess more of a linker feature is if you declare a function as __weak__, you can check it at runtime for == NULL to determine if the application was built with the function defined.

That's non-standard.

Re: Some Obscure C Features

#38

Most of these are due to the cruft added in C99 and later. Compile-time trees are possible without compound literals. More than twenty years ago, I made a hyper-linked help screen system a GUI app whose content was all statically declared C structures with pointers to each other. At file scope, you can make circular structures, thanks to tentative definitions, which can forward-declare the existence of a name, whose…

Oops I'm wrong, I meant compile time anonymous trees. It's very obviously possible by defining other variables.

Re: Some Obscure C Features

#39
post #13

The preprocessor trick of passing function macros as parameters is not that obscure. I have seen it used and I've used it myself. It is very useful when you have a list of static "things" that you need to operate on. Say I have a static list of names and I would like to declare some struct type for each name. I also would like to create variables of these structs at some point, and I would always do so for the entire…

It can be both useful and obscure ;-) I realize it's not black magic, it's just uncommon.

I used this trick to declare and generate code for an entire parser, it's lovely.

I recently switched to the other style of C-macro though:

  #define X(A, B, C) ...
  #include 
  #undef X
This style is less powerful but doesn't require the annoying \ at the end of lines.

Re: Some Obscure C Features

#40
The article has examples of array parameters like

    int b[const 42][24][*]
but you can get more fun with variably modified array parameters instead of just constants like 42. For example,

    double sum_a_weird_shaped_matrix(int n, double array[n][3*n]) {
        double total = 0;
        for (int x = 0; x 
has a variable and a more complicated expression in those positions.

But those variably modified parameters can have arbitrary expressions in them, like

    int last(size_t len, int array[restrict static (printf("getting the last element of an array of %zu ints\n", len), len--)]) {
        return array[len];
    }
C++ denies us this particular joy which could have made function overload resolution even more fun.
Post reply on HN