6 ms·
Zig is a lot more complex than C, but I think most of the language complexity came from filling in gaps rather than tacking things on. For example I think they
by dbandstra 6y ago
Zig is a lot more complex than C, but I think most of the language complexity came from filling in gaps rather than tacking things on. For example I think they have settled on having no dynamic dispatch feature (interface, trait, virtual method, etc) at the language level. The comptime feature is flexible enough that userland approaches seem to be good enough. I bet a lot of "complexity" in the future will come from competing idioms used in various third party libraries.
- MaxBarraclough 6y agoC is unusual though in that it's a minefield of undefined behaviour. It's very easy to think you truly understand the language but to have no real understanding of its many curious rules around undefined behaviour. You can't take a try it and see attitude, you need to be a language-lawyer. Of course, even if you understand the rules you'll still accidentally write code that invokes undefined behaviour, which is much of the reason languages like Zig exist.
- pjmlp 6y agoC is a great language for pub quizzes, because so many think mastering K&R C book is all it takes.
- MaxBarraclough 6y agoHere are your pub-quiz questions: https://www.gimpel.com/archive/bugs.htm https://www.gimpel.com/archive/bugs.htm
- pjmlp 6y agoI remember when Gimpel used to do magazine ads with those kind of questions. Most DDJ or The C/C++ Users Journal issues will have them.
- rightbyte 6y agoThe amount of language-lawyer UB bugs vs all other bugs for me is like zero, when I write C. I can't actually recall any language-lawyer bugs.
- lmm 6y agoHow do you know? One of the reasons they're so insidious is that code that hits them tends to work fine until it gets compiled with a newer version of the compiler. E.g. signed integer overflow did exactly what you expect in most compilers until fairly recently.
- steerablesafe 6y ago> E.g. signed integer overflow did exactly what you expect in most compilers until fairly recently. How recently? Both gcc 4.1.2 (2007) and clang 3.0.0 (2011) optimizes `x+1 > x` to true for a signed int `x` on -O1. And it probably goes way back, these are just the oldest compilers I found on godbolt. https://c.godbolt.org/z/sdd15c https://c.godbolt.org/z/sdd15c
- lmm 6y agoAh, point taken, but that's within the bounds of what many people expect; propagating the fact that the overflow is "impossible" to rearrange earlier control flow is more surprising and more recent.
- steerablesafe 6y ago> Ah, point taken, but that's within the bounds of what many people expect; The thing is it's very hard to draw the line once you go that route. Different people expect different things from undefined behavior. The best thing is to not expect anything sane. And if you are unhappy about certain undefined behaviors in the standard then it's better to push the standard to define more behavior. Certain unnecessary undefined behaviors get resolved with newer standards, although I would expect significant pushback on defining the behavior signed integer overflow.
- shp0ngle 6y agoZig also has undefined behavior. See whole section here https://ziglang.org/documentation/master/#Undefined-Behavior https://ziglang.org/documentation/master/#Undefined-Behavior the difference is, they try to check for them at compile time, and if you compile with safety checks, also at runtime; however, if you compile with ReleaseFast (which I assume most people will in production), those runtime checks are turned off and the undefined behavior still exists.
- kristoff_it 6y ago> (which I assume most people will in production) No, people are supposed to use ReleaseSafe, especially "in production". You can still disable runtime checks in specific execution paths. ReleaseFast is for applications where there are no disastrous consequences to a bug in the code, like videogames.
- MaxBarraclough 6y agoRight, I hadn't meant to imply Zig is free of UB. It aims to improve on C's wild-west UB rules not by having no UB, but by having only a manageable dose of it, and supporting good optional runtime checks. Zig's approach is essentially that of Ada. You can ask the Ada compiler for runtime checks, or promise the compiler that your code is free of undefined behaviour and have your code run at the speed of light (C), or go haywire if you got it wrong. Sadly C is less suited to runtime checks, arrays are dealt with through raw pointers so range checks aren't easy for the compiler to add automatically. You can though ask GCC to generate checks (trap-on-failure) for things like dereferencing NULL, or signed arithmetic overflow.
- blackrock 6y agoHow does C programmers handle integer overflow? You can’t really check if an integer variable is greater than 2^32, since that itself would cause an integer overflow.
- MaxBarraclough 6y agoThe overflow happens in the computation, you don't typically determine whether there was an overflow by checking the variable into which the result is saved (although perhaps this approach could work if you're using an unsigned integer type, which wraps around on overflow). You'd normally perform the check before you perform the addition (or whichever operation risks overflow). This can be fiddly to get right, but it's possible. Also, the maximum value that can be represented in a variable of type uint32_t is (2^32) - 1, not 2^32.
- andi999 6y agoCan you be more specific? Since an integer cannot be greater than 2^32 checking for this doesnt make sense.
- patrec 6y agoThe most convenient way is to probably to use a compiler builtin https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins..., if you want to be portable the next easiest way is to use a wide enough type (e.g. add or multiply two 32 bit numbers to a 64 bit one and verify it is inside [INT_MIN, INT_MAX]). Otherwise, you can either do a pre-condition check (for addition overflow occurs if a > 0 && b > 0 && a > INT_MAX-b || a < 0 && b < 0 && a < INT_MIN-b) or work with unsigned integers and check for wraparound after the operation. Finally, both clang and gcc have options to check for signed integer overflow at runtime (-fsanitize=signed-integer-overflow for gcc). Of course, in practice this is too much effort for most people most of the time, so actual deployed C and C++ code is full of undefined behavior due to integer overflow. This paper has a great overview: https://wdtz.org/files/tosem15.pdf https://wdtz.org/files/tosem15.pdf
- deleted 6y ago[deleted]
- stock_toaster 6y ago> Zig is a lot more complex than C True, but on the other hand... C _does_ have preprocessor macros, which from a pure language standpoint adds a lot of complexity.
- audunw 6y ago> Zig is a lot more complex than C, but I think most of the language complexity came from filling in gaps rather than tacking things on. I think it's important to keep in mind that with C you might end up using a bunch of extenions, complex pre-processor macros or pragma magic to achieve essential things. C in itself is relatively simple, but using it in practice can end up becoming quite complex. Zig ends up being simpler in some ways by having less magic and special ways of achieving things. "printf" is very magical in most C compilers, but the equivalent in Zig is nothing particularly special from either the language or the compilers side. I think the only thing that truly adds complexity without being strictly necessary, is the async stuff. But you can argue that async IO is becoming a really essential thing for systems programming, and using it without explicit language suppport is a nightmare.
- dnautics 6y agohaving just learned it, async is not really complex, so much as "a thing that needs to be explained to you" kind of like how "pointers need to be explained to you". Sadly I don't see any good resources on that, yet.
- MaxBarraclough 6y agoDisagree, async (edit: in .Net at least) is pretty complex. For instance, from my experience it seems few C# programmers know that it's possible to deadlock if you get it wrong. [0][1] Plenty of people get confused with the basics, too. [2][3] [0] https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html https://blog.stephencleary.com/2012/07/dont-block-on-async-c... [1] https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/... [2] https://stackoverflow.com/a/34681101/ https://stackoverflow.com/a/34681101/ [3] https://stackoverflow.com/a/34799307/ https://stackoverflow.com/a/34799307/
- dnautics 6y agoZig is not .net. async in zig is simple, you just need to understand a few things: what a frame is, what the difference is between a stack frame and a heap frame, what it means (at the machine level) to jump between frames, and the fact that you can't do it in c. That's it! It really is just a control flow structure in zig (which is why it's a keyword)
- dnautics 6y ago> Zig is a lot more complex than C Well if you include preprocessor, make, and cmake (which I think is basically standard now), the level of complexity feels "close to parity".
- MaxBarraclough 6y agoIncluding CMake is downright unsporting ;-P
- dnautics 6y agoMost modern professional C projects do use it. You kind of can't get away.
- MaxBarraclough 6y agoWe're agreed. I use it for my personal C++ projects. It's really quite awful (I've ranted about it on HN more than once [0][1]) but it's still the least bad choice. [0] https://news.ycombinator.com/item?id=24203172 https://news.ycombinator.com/item?id=24203172 [1] https://news.ycombinator.com/item?id=24565266 https://news.ycombinator.com/item?id=24565266