(Modern) C Programming Tips and Tricks


I haven’t found one I really liked enough to use, but there’s nanopb if you’re looking for something for microcontrollers: https://koti.kapsi.fi/jpa/nanopb/


msgpack for mcu’s

light weekend reading…


would like to open up this again with a kinda related topic to serialisation. file identification. looking to find a simple way to manage files that are linked in other files, in this case samples that are used in sequencer patterns, and similarly patterns used in compositions. could creating some kind of hash tables be a good start? or maybe a simple route with just file name comparison goes a long way? and I should probably add that this is for files saved on fat32/memory cards, just beginning to read up on fat and fat directories, still stumblin’ unfortunately.


I’ve been able to get away with loading a linebreak-separated list of paths where each path is followed by an integer index representing its intended position in storage. Then a pointer array to whatever data structures I’m using to represent the audio samples.


Normally when managing related files I always store the relative path to the “master” file. Ideally the path is a descendant of the master file’s location and you don’t need to traverse up the tree. That way the whole project can just be archived and moved elsewhere and the references remain intact.

I’m not really sure what you want to do with hash tables though?


thanks for the replies! looks like a good start would be to figuring out how to handle paths!

about hash tables, I was thinking about how ProTools use unique identifiers for audio files. ProTools handles paths too, of course.


updated link for the linux style guide:


I’m just breaking up the handler_ScreenRefresh function (500+ lines) from the Teletype source code into 1 function per mode. So this from the kernel coding guide seemed apt:

Functions should be short and sweet, and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24, as we all know), and do one thing and do that well.

My tip to help with too long functions is to bump the font size really high, i.e. so you can only see about 25 lines of text (and turn off any ‘minimaps’ and such if you’re abusing them). This has the added benefit of helping your eyes and posture out too!

And while I’m on my soapbox… can I request no more commenting out of code, just delete it. Git (and GitHub) are great for navigating old code. Commented-out code is a kind of technical debt really.


I like those guidelines apart from line length. I usually use around 120 - 140 characters, it allows more descriptive variable names without really sacrificing readability.

Agree about commenting out code, I never do it. Always delete. I’ve never regretted deleting code. Commenting out leads to confusion.


Lots of great things in this thread.

My favourite tip is function pointers. Really learning function pointers has changed C as a language for me - it’s not quite as expressive as Scheme/Python/anything else with closures, but it makes abstractions possible that can clean up code and force you to think about the logical structure of your problem and avoid a lot of redundant and superfluous repetition. It’ll also help force a nice uniformity in terms of types: turning a big muddy case statement into a list of separate functions that all take the same arguments and return the same thing has proven beneficial not just for readability once the code is written, but also for my own analysis of what actually needs to be done.


A question for the experienced C programmers here…

If you have a global variable that is only used within a single function is it preferable to declare that static in it’s narrowest scope (i.e. in the function) or to leave it global?


thank you for the thorough review and education. i’ve become a much better cooperative programmer through your (and many others @zebra @ngwese etc) comments and code revisions.

for the record i had no illusions that my code was even close to top notch when i open-sourced it. i’m grateful for the patience you’ve all had with it, and i hope my reputation hasn’t suffered too badly :wink:


If the use of a static variable will never escape the function then I’d localize it. If there is any risk of its address or the memory associated with that local static escaping the function then I’d keep it global.


Nah, you’re reputation is more than intact. My hard disk is so very very full of unfinished bits of code… so finishing something, releasing the code, and then being so open to contributions, you should feel chuffed.


I wouldn’t sell yourself short. I think it is a lot more difficult to start from a clean slate and create something which hasn’t existed before. When I personally start from scratch it shows in the code, things are messy, ideas evolve, and the target changes.

I’ve also had the privilege of working with other people improving/refining stuff that I’ve written. With the large picture already established they can focus their energy in areas which I simply couldn’t. They apply experience that I lacked. The end result was (nearly) always better.


One thing I’ve learned to love is not commenting out code but #if 0’ing it out. Two reasons: first, anytime you identify a bit of code you’re going to change, #if 0 it out, #else your new changes. Now testing your new implementation against the old one–which I always find I wished I hadn’t deleted, and don’t want to go git-wrangling to get it back–is just flipping that condition. Clean up after yourself once done. This is the one thing that makes me want to jam the c preprocessor onto everything.

The second reason is maybe more sinful, but using it as an actual alternative to commented out code. Commented out code is always a bit weird–always seems to be in the wrong place, an artefact from an earlier revision, whatever. But #if fixes it in place: it’s not a hazy ghost from the past, switching it on should enable or change some feature, and it should compile and work. Especially good for debug code that you keep rewriting every time you revisit a bit of code. Of course this requires discipline, but so much of programming boils down to discipline anyway. I should add that I’m otherwise totally opposed to the horrible interleaving of implementations controlled by compile flags–blegh–but this is ok, in moderation


I also like to use #if 0. Though usually it’s for a very brief period of time while I test a small chunk of code. I don’t like them to survive a commit.


If you never take the address, then use the static version. Limiting scope is also limiting cognitive overhead so I think it’s best to do so whenever possible.


Yeah, it gets confusing quickly if it’s committed. That’s why I mentioned I don’t like to actually commit the defines. Once I decide if I prefer the code with or without the #if 0 section I will delete the define. If there’s a few different ways to do something I prefer to contain them in their own functions or whatnot and then use a runtime flag to decide which to use. I’ve found that approach is less applicable to designing synths and whatnot but it was very useful when deploying networked applications to be able to turn features on and off or try them on a subset of machines.

A big problem with commented-out code or #if 0 blocks that survive for any length of time is that they tend to rot. Since the code is not being compiled or run, its behaviour will fall out of line with the rest of the program as it evolves.


So with static variables, should one consider defining them at the top of the function even if a narrower scope would suffice? I’m trying to put myself in the shoes of someone reading the code for the first time, would they want to know upfront…? I think I would.

Absolutely, I often find myself using GitHub’s blame / history UI. It’s surprisingly good.

Even having #if DEBUG in your code can cause all manor of problems, you end up with different bugs in each of your code paths. There’s a really annoying heisenbug in the Teletype USB memory stick code, turn the debug statements on in the MIDI code and it works, turn them off and it doesn’t.