Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

To answer your questions:

* Clang vs gcc: doesn't really matter. Clang has slightly better compile times. GCC has really advanced but in some cases still has inferior warning/error messages. Best practice is not to really use GNU c features unless you want to. If you do want to, decide if you want to just use the subset supported by clang or the full shabang from gcc. Beyond that: if you want to support both, test on both before release and do release builds using whichever produces faster or smaller (choose whichever metric you prefer) binaries.

* Build systems: plain old make is sufficient in most cases. I would recommend autoconf over cmake just because it's significantly simpler, although cmake has better cross-platform support (it can generate visual studio build files, for instance). I wouldn't use something outside of make, cmake, or autoconf, even if it seems like it's better in every way than those, because it probably isn't, and even if it is, you lose out on the battle-tested, available-everywhere, and widely-used nature of the above build systems. I shouldn't have to install a build system to build your program, and if I do, I should be able to use that build system to build a significant of other programs too.

* Linting: not really necessary IME. Aim for no compiler warnings, though.

* Yes, use valgrind. Anything it complains about, fix. Uninitialized values and out-of-bounds memory accesses are significantly more important and worrisome than memory leaks (because they represent potential attack vectors), but still, it won't complain about something unless it's actually something that should be complained about.

* Testing: like the other commenter said, just a little bit of macro magic and you're golden.

* Code organization: headers in include/, source files in src/. You can separate src/ into subdirectories if your project grows to sufficient complexity that the source files become difficult to wrangle. Separating the headers into separate directories is probably not necessary, however.

* Not directly c-related, but pick a SANE code style and stick to it.

* Debugging: make a separate target that compiles in debug symbols and disables optimizations, and use gdb (or lldb) on it. You don't need much to get started: 99% of the time, all I do is "break main", "run" ("r" for short), "backtrace" ("bt" for short), "frame <number>" (to switch between stack frames), and "print <variable>" ("p <variable>" for short); it's taken me quite far.



> Uninitialized values and out-of-bounds memory accesses are significantly more important and worrisome than memory leaks (because they represent potential attack vectors)

I agree with your conclusion but not your reasoning. Attack vectors are way down the list of reasons why uninitialised values and out of bounds memory accesses are higher priority. They're higher priority because they will likely (maybe definitely) crash your program or (even worse) cause it to fail in a frustratingly indeterminate manner. You should be so lucky that your program becomes significant enough to be targetted by "attackers".


Agree. One such simple example is a ListNode (Linked List). In C/C++ implementation, if we don’t explicitly declare

    node.next = null
It can have a garage unless you assigned a value.

This can be hard to debug in production.


I like your thinking regarding using simpler build tools. I took this to the extreme and now my build tool for personal C/C++ projects is just a file called build.sh/build.bat. That does little more than:

    gcc main.c
main.c #includes any other .c files that are needed (the term for this appears to be a 'unity build'). Compiling this way is /really/ fast, which is why it's okay to use a dumb build script that always recompiles everything.


I would tbh recommend make (bsd make is a wider-supported subset of gnu make) over a shell script, because it can automatically detect which source files need to be rebuilt and which can be kept from previous builds, greatly reducing compile times. Also, it supports parallel builds, although this one is relatively easy with a shellscript.


If the sole purpose is to track which files changed, then redo is arguably a simpler solution to this problem - you don't need to learn a new language either (and its funky rules like the tab/space difference), just shell scripts with a couple new commands.

http://news.dieweltistgarnichtso.net/bin/redo-sh.html


I've never seen this before... I shall have to try it out on a C project I'm starting. Thanks!


I do exactly the same thing whenever I write C/C++. Genuinely large projects aside, I see no compelling reason to waste your time with build systems. This approach is simple, easy to maintain and lets you get on with writing actual code.


Out of curiosity, why not use make for something so simple instead of a Shell script?


Because I don't see any value from using it. It's not easier to read or write, it doesn't force you to keep it sane and simple like a bash/cmd script generally would, and it's just another dependency I don't really need. If I take a minimalistic approach to build systems, I prefer to go all the way.

Also, although this is merely a personal quirk that shouldn't persuade anyone else, I've seen enough horrific, unreadable make files to instinctively dislike them by now.


In my mind, shell scripts are simpler. I have no reason to need the extra complexity that make brings.

Also, I do a lot of programming on Windows, where GNU make would be another dependency to install. (Also, in my experience, make is slow on Windows, since they have to emulate fork()). I guess I could use Microsoft nmake, since I assume it's still installed along with Visual Studio, but again, batch files are simpler.


Take a look at this: http://news.dieweltistgarnichtso.net/bin/redo-sh.html

And for GNU tools on Windows, I would heavily recommend MSYS2 these days - having Pacman as the package manager is very nice, and there are already a lot of packages there.


My thoughts are a shell is usually installed by default. Make may not be. This is only a guess. I'm not sure myself.


Out of curiosity, why did you not choose make to do this?


did you watch handmade hero? he advocates the same


Haha, yes. Casey has been quite a big influence on me re-finding enjoyment in programming that I thought I'd lost.


> Clang vs gcc: doesn't really matter.

Clang seems to have better sanitizer support, which can be lighter weight initial passes before running valgrind.


What exactly is better wrt. to sanitizers with Clang?


I would also like to know. I have been using the latest versions of both clang and gcc and have not noticed any differences.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: