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…
I haven't heard of "tentative definitions" before. Couldn't you just replace it with a regular declaration i.e. extern foo n1, n2; Is there any benefit of tentative definitions over this?
Some Obscure C Features
111–120 of 147 posts
Re: Some Obscure C Features
#112Earlier quoted context omitted.
Another neat note IIRC is that array parameter sizes don't actually do anything, they just are there for semantic purposes and get treated as raw pointers. So if you do void func(int x[10]); You're free to call it like int k[5]; func(k); And you won't get any warnings. Unsettling!
That's what the static keyword means in those array declarators. void func(int x[static 10]); must be called with an argument that is a pointer to the start of a big enough array of int. I can't get recent GCC or Clang to warn on violations of this, though.
void foo(int *p)
{
func(p);
}
How can the compiler know if `p` points to space for 10 integers?Re: Some Obscure C Features
#113Re: Some Obscure C Features
#114Re: Some Obscure C Features
#115Earlier quoted context omitted.
IIRC these are a legacy of BCPL
They weren't in K&R. I recall a story about a standardization meeting, on the way to ANSI C, where representatives from some European(?) country that didn't have some of the necessary punctuation on their country-specific keyboards, essentially snuck the trigraphs into the spec when the other representatives weren't looking.
Re: Some Obscure C Features
#116I was reading the source code for a NES assembler written in pre-C99 C, and there was an odd C feature used in it that I haven't really seen anywhere else. It was before C had built-in booleans and the author had defined their own, but true was: void * true_ptr = &true_ptr; true_ptr is a pointer to itself. So however many times you deference it: printf("%p\n", true_ptr); printf("%p\n", &true_ptr); printf("%p\n", *((v…
cdecl> explain const void * const_pointer declare const_pointer as pointer to const void cdecl> explain void * const const_value declare const_value as const pointer to void cdecl> The first must be the one that segfaults on write, IFF the compiler chooses to place it in the .text (as it should).
Testing it on my machine with the following code seems to validate this hypothesis.
//file: test.c
#include
const void * const_pointer = &const_pointer;
void * const const_value = &const_value;
int main()
{
printf("%p\n", const_pointer);
*(int*)const_pointer = 0;
printf("%p\n", const_pointer);
printf("---------------------------\n");
printf("%p\n", const_value);
*(int*)const_value = 0;
printf("%p\n", const_value);
return 0;
}
Result:
$ gcc test.c
test.c:4:30: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
void * const const_value = &const_value;
^
$ ./a.out
0x55b29ebfc010
0x55b200000000
---------------------------
0x55b29ebfbdb8
Command terminated
As to why the first one doesn't also result in a segfault, I don't know.Re: Some Obscure C Features
#117A more obscure feature is the uncommon usage of comma operator. We often use the comma operator in variable declaration and in for loops. But it can also be used in any expression. For instance, the next line has a valid C construct: return a, b, c; This is particularly useful for setting variable when retuning after an error. if (ret = io(x)) return errno = 10, -1; The possibilities are endless. Another example: if…
Re: Some Obscure C Features
#118The 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…
enum foo {
FOO_THING_ONE,
FOO_THING_TWO,
FOO_THING_THREE,
...
FOO_THING_SEVEN_HUNDRED
};
// Using concatenate '##' and stringify '#' operators
#define FANCYCASE(X) case FOO_THING_##X: str=#X; break
const char *foo_to_str(enum foo myFoo)
{
char *str;
switch(myFoo)
{
FANCYCASE(ONE);
FANCYCASE(TWO);
FANCYCASE(THREE);
...
FANCYCASE(SEVEN_HUNDRED);
}
return str;
}Re: Some Obscure C Features
#119I found the most interesting one to be compile time trees. Does anyone have any good use cases for it?
https://elixir.bootlin.com/linux/latest/source/drivers/clk/s...
Re: Some Obscure C Features
#120> 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.