What is “:-!!” in C code?
stackoverflow.com
stackoverflow.com
#define BUILD_BUG_ON_ZERO(e) (sizeof(char[(e) ? -1 : 0]))
which, when it fails, results in error: size of unnamed array is negative
which is no worse than the provided code which results in error: negative width in bit-field '<anonymous>'
It's worth noting that declaring an array with 0 elements is not allowed in C99. However, using a struct with no named members has undefined behavior in C99 [1].[1] http://stackoverflow.com/a/12918937/959866
Edit: You can get around having 0 elements by using
#define BUILD_BUG_ON_ZERO(e) (sizeof(char[(e) ? -1 : 1]) - 1)
but you're starting to lose clarity again.For example, this compiles but segfaults at runtime (GCC 4.9.2):
int main(int argc, char * argv[]) {
return (sizeof(char[(argc) ? -1 : 1]) - 1);
}
[1] https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.htmlThe negative-length array trick is used in the Linux kernel source code [1]. I expect it's compiled with -Wvla though.
[1] https://github.com/torvalds/linux/blob/v4.5/arch/x86/boot/bo...
[0] - ISO 9899:2011 Programming Languages - C 6.7.6.2 4
-#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1)
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
The commit that introduced the notation, actually used something similar to your second approach:
https://github.com/torvalds/linux/commit/8c87df457cb58fe75b9...Moreover, this particular example is due to compatibility issues across the multitude of platforms that Linux supports, necessitating support for versions of C older than any language you might like. Were Linux only ever to be compiled with a C11 compiler, this could be replaced with simply `static_assert(x)`.
And speaking as someone who's coded C for 16 years and makes a living doing so: it is rare that I write a macro; my Makefiles are about 5 lines long; I've never touched automake; and the only GCC flags I ever use (and use consistently) are `-std=gnu11 -Wall -Werror -O3`. Heck I wrote a cross-platform NES emulator last month following these rules.
There's a lot of bad C out there to find. Don't let that turn you off from writing good C.
The whole advantage of C is that it is a powerful, nice, and portable way to express roughly the same things you would otherwise express in assembly. Most of the really nasty undefined behaviour of modern compilers comes from forgetting this.
Nothing fancy, but it means that the language can be compiled in a straightforward manner without any runtime support and a programmer can easily have a complete mental model of it.
And Algol, PL/I and their respective variants are around 10 years older than C.
But I still say that C is a way of talking about same things you want to talk about in assembly, albeit while automating some tedious but important things like register allocation. You are still commanding the computer at a low level: still telling it which byte to put on which IO port or memory location.
Of course you can build higher level abstractions on this -- but only to a point. C compilers go wrong when they imagine a C program lives in such a higher level of abstraction -- whereas the other languages thrive on defining such abstract machines.
If I declare a variable in the middle of a function, I am declaring intent that this variable shouldn't be used in the first half of the function – maybe the data it is supposed to hold cannot be available at that point.
Those code styles as still prevalent in 2016, speaking from the experience of occasionally having to look into how people write C code to integrate into Java, .NET, Python, Ruby at the enterprise level, which just stresses my point of view about the language.
Specially since outside HN bubble many don't even know what a static analyser or code review are all about.
It provides a lot of things on top of a simple makefile...
You don't need it to write C, but everything that uses it is not "bad C code".
At some point, even for a compatibility focused project like Linux, that cost must get too big for the benefit it provides.
The problem is that C is in everything. OK, not literally, but you know what I mean.
Bad JavaScript is easy to avoid. You just navigate to another website. Bad C though, not so much. Often times it's in the kernel or somewhere deep like that.
Like it happened with the browser and JavaScript, C got adopted thanks to UNIX.
Before UNIX's adoption, many of us had a pleasant coding life using Turbo/Quick/Apple Pascal, Modula-2, Basic compilers.
(Similar misunderstanding to the "down to operator" --> https://stackoverflow.com/questions/1642028/what-is-the-name...)
"down to operator" = unhelpful use of whitespace. postdecrement x, check if >0
I still find C one of the easiest to pick apart. Obfuscated C entries excepted!
It's easy to forget how much of that mindless preprocessor and make conditionality we've left behind. Sadly in exchange for hardware that's much blander now.
The rules are simple for simple projects. Then you get into things like undefined behaviours, implementation-specific behaviours, etc. Which compilers will abuse heavily for optimisations without telling you about it - for example like the case of silently removing NULL checks. Also any undefined behaviour at all in your source code allows the compiler to throw away all code after it. Without telling you about it.
The general rules of macros are:
1. Don't use them; prefer static functions.
2. Use them as symbolic constants only.
3. Use parameterized macros only when you must use # or ## (i.e., for code abstraction), and then use them sparingly.
4. If you really insist, wrap every use of the arguments in parentheses, and be careful not to write the name of an argument somewhere where you don't mean it.
You'll get pretty far knowing next to nothing about macros (e.g. expansion phases and tokenization rules) by following the above rules.
Though, upon further examination C++11 was published 1 month before C11 (September and October, respectively). I guess your point still stands.
The problem with C IMO is that the compilers are very permissive by default and it's easy to trigger an undefined behaviour with seemingly harmless code if you're not careful. Things like promotion rules make it difficult to guess at a glance how the code is going to behave if you don't have a very good understanding of these (rather quircky) rules. There's also the whole mess of the pointer vs. array distinction which sometimes matter and sometimes doesn't etc...
By comparison these BUILD_BUG_ON macros are relatively straightforward IMO. The naming is a bit misleading unfortunately but at least it's in full CAPS so you know it's a macro...
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()
Where does the alert come from?
function range_error_on_zero(x) {return Array(-!!x).length}Neither Go nor C has static assertions, the macro in questions uses clever techniques to implement one in C, but you probably can't do that in Go. So C is more powerful here.
Another example of clever C macro use is implementation of foreach loop (C doesn't have foreach) in linux kernel, here's question about it:
http://stackoverflow.com/questions/15754236/how-do-i-use-the...
> Neither Go nor C has static assertions
C11 has: _Static_assert(const_expr, fail_string)I haven't really used C after I graduated and I remember my professor showing us how to implement static assertion :)
[ Disclaimer: I'm a maintainer of runC and have been programming in Go for many years. But that doesn't mean I have to like the language. Give me C any day. ]
Personally I think Go actually makes a pretty bold statement about dependency management: people don't know what they want. I also have a sneaking suspicion Git and Github's interface in no small part to blame.
Golang for me made me come to one important realization: if I can't build a project by just cloning the git repository, then why the hell not?
If you have not experienced this before, try cloning a repo that uses Go-1.6-style vendoring anywhere outside the GOPATH.
_Static_assert(condition, "error message if condition evaluates to false");
(You can also use "static_assert" as an alias for the ugly keyword after including <assert.h>.)Edit: whoops, apparently static_assert is in C11 as well as in C++11. Ignore this comment.
There's also a fun compile time check for arrays: https://github.com/torvalds/linux/blob/1001354ca34179f3db924...
http://stackoverflow.com/questions/1642028/what-is-the-name-...
int main() { int x = 10; while (x --> 0) // x goes to 0 { printf("%d ", x); } }
I know Fortran 90 and use it for some simulations -- since it has a matrix syntax it's not that hard to write changing matlab code. But I know Fortran is going where the wild roses grow, and wonder if I should spend the time to learn C for high-dimensional numerics.