4 ms·
Jesus Christ. Let the reader beware indeed. There are horrible suggestions here that will result in bad code if you think "Hey, that's a cool suggestion, I'll t
by fsloth 13y ago
Jesus Christ. Let the reader beware indeed. There are horrible suggestions here that will result in bad code if you think "Hey, that's a cool suggestion, I'll try that!". Or "Hey, I read in that blogpost this is OK in C, I'll do this".
For instance:
Stack allocation. It's seldom OK to use static variables even with accessor functions. Wrap the state that the functions operate in structs that you pass to functions. There are a few places where you cannot avoid effectively global variables (usually external resources like sockets, window handles etc) but those should be wrapped in their own subsystems and not leaked all around the place in global variables. "Still, this is one of the better reasons to use a global variable." - congratulations, you are on your way to Spaghetti Code land. Would you like some black pepper with your Complexity Bolognese? Protip: Wrap it in a struct and pass it as a variable. Or redesign your module dependencies.
Too much state in your God-Object. Getting confused? Need better design. C is not an excuse not to write modular, understandable code where the program flow is obvious.
Yes, it can get really verbose. If verbosity is an objection, then do not write in C.
Or, if you dislike verbosity, do what so many others have done. Write your core routines in C and then write the high level logic in Lua.
Please excuse me my theatricality. I'm not assaulting the author. I'm rather agravated that this post has this level of visibility and the potential damage it can do.
- jamesaguilar 13y agoAnother example. Working "correctly" with threads is not something I would typically think of as a distinguishing feature of stack allocation. All basic c allocation techniques work equally well with threads. That is, they all work fine when only one thread attempts to perform transactions on the memory, and none of them do if more than one does (sans locks, of course).
- mpyne 13y agoOn the contrary, every thread has its own stack. In fact the stack was the original "thread local storage".
- jamesaguilar 13y agoYes, my point is that if you try to operate on memory from another thread's stack, it is equally unsafe as operating on memory from the heap you received from another thread. The allocation method is irrelevant to whether it is safe. What is important is whether it is being accessed concurrently.
- ANTSANTS 13y agoNethack dates back to 1987 (as the article says, predating ANSI C) and the same codebase is still in development today. You need to keep that in mind when reading the article. Nowadays, yeah, just using a garbage collected language would solve the vast majority of their problems with negligible effects on performance (it's a roguelike), but it wasn't really an option when the project started, and rewriting it now isn't acceptable either. This article still full of interesting tricks to have up your sleeve (comically unsafe things like the "ring buffer of objects" aside) in case you're in a similar situation as the Nethack devs were in the 80s and 90s, pushing the boundaries of what is possible in a video game/computer program of your choice on limited hardware. >I'm rather agravated that this post has this level of visibility and the potential damage it can do. Potential damage, really? The people naive enough to try everything listed in this article probably aren't programming in C to begin with these days. The average Fancy New Web Framework That Will Change Everything Again article hurts way more than this.
- pessimizer 13y ago>Nowadays, yeah, just using a garbage collected language would solve the vast majority of their problems with negligible effects on performance The person you responded to didn't suggest this.
- userbinator 13y ago> It's seldom OK to use static variables even with accessor functions. Wrap the state that the functions operate in structs that you pass to functions. Why? I note that the word "because" doesn't appear once in your post.
- fsloth 13y ago> I note that the word "because" doesn't appear once in your post. Sorry, my rant was a fairly long to begin with :D To clarify my position, I'm maintaining and developing as my day job a few MLOC CAD application that is two decades old which has core written partly in C. This is one of the most painfull antipatterns plaguing that codebase. It's seldom OK to use static variables even with accessor functions. Because: 1) Understandability. Suddenly it's non obvious what the state actually is that your code modifies. Functions are not functions that get input and process output, but become detached nodes in a non-obvious call graph. Yes, it's embedded in the stack, and, yes, you can figure out ways to figure out what is actually happening but it's not obvious from the code. There are quite a few non-obvious things affecting a real world production code. It does not help to add a few non-obvious patterns which are not necessary. I.e. this pattern creates unnecessary accidental complexity that hinders all velocity. 2) Suddenly it's impossible to structure the code to parallellize it. The explanation is fairly long but if it's non obvious I can return to this point.
- exDM69 13y ago> Please excuse me my theatricality. I'm not assaulting the author. I'm rather agravated that this post has this level of visibility and the potential damage it can do. I thought it was a good post and provides a good overview of different methods along with a discussion why you should or shouldn't do some of these things. I'm pretty certain that in the Linux kernel you can find each and every one of these methods used more than once and for a good reason. Maybe in a normal user space app you have the luxury of being able to malloc/free everything and have nice high level containers, etc available but when writing C code, the assumption of normal user space apps is not necessarily true. All of these methods - and when not to use them - is very useful knowledge.
- Nursie 13y agoIt's amazing how much you can achieve without malloc and free if you're just careful to pass in the right output space and buffers as part of your function call. You rarely really need them for most functional code. (Non-trivial data structures may be different...) And the OP is right - globals, especially if not encapsulated within a single code module, are a prime cause of opaque spaghetti code.
- ballard 13y agoRemember vintage Fortran. So I made once had the displeasure of doubling the performance of a fission reactor safety simulator by disabling win32's swap... 30 years and 2e7 LoC codebase. >.<. The working set was nowhere near smart, but it did work faster than AIX, HP-UX, SCO & Solaris/SunOS ports.
- adestefan 13y agoI've written plenty of embedded code where the first rule was "Thou shall not malloc." In just about every case it turns into setting aside static buffers and doing manual structure allocation. This is such a common idiom in embedded systems that most RTOS systems have this functionality built-in.
- Nursie 13y ago