Earlier quoted context omitted.
> If the operand is the name of an object [...] what does it mean to evaluate `n`? When you say "n", syntactically, you have a primary expression that is an identifier. So you follow the rules for evaluating an identifier, which will produce the value. C doesn't describe it very well, but the value of the expression is the value of the object. In terms of how it is implemented in actual compilers, this would mean iss…
Sorry, in the first example I meant to write (adding a declaration and initialization for n): int n = 42; int vla[n]; sizeof vla; not `sizeof n`). (It doesn't look like I can edit a comment.) Logically, evaluating the expression `vla` would mean reading the contents of the array object, which means reading the value of each of its elements. But there's clearly no need to do that to determine its size -- and if you ac…
Some Obscure C Features
121–130 of 147 posts
Re: Some Obscure C Features
#122The 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 express…
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!
The C FAQ is pretty old though, I’ve always wondered how much of that advice changed in C99/C11... from cursory googling things don’t seem to have changed much.
Re: Some Obscure C Features
#123A 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…
The comma character as a token in variable is decalararions is not the same thing as the comma operator.
Re: Some Obscure C Features
#124Most 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?
Re: Some Obscure C Features
#125I 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…
const void * const_pointer = &const_pointer;
void * const const_value = &const_value;
I've never really understood why people put const before the type, to me the following is far more obvious: void const* const_pointer = &const_pointer;
void* const const_value = &const_value;Re: Some Obscure C Features
#126A 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…
Surprisingly, you can override it in C++. I haven't seen anyone do it, but you can. If you find a good, productive override for the comma operator, please post about it.
Re: Some Obscure C Features
#127Earlier quoted context omitted.
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).
My (admittedly naive) understanding of the ordeal leads me to believe that it is that the 1st would not segfault but the second will since declaring it as a const pointer will create additional memory constraints. 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() {…
Edit: Actually I don't think the pointer is put anywhere, rather its value is stored in .data (non-read-only data) so it can be mutated without issue. Again though, my assembly isn't amazing.
Re: Some Obscure C Features
#128 int x = 10;
while (x --> 0) {
printf("%d ", x);
}Re: Some Obscure C Features
#129Re: Some Obscure C Features
#130The 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…
Here's one of my favorites for dealing with logging repetitive enum names and the like: 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(SE…