7 ms·
I think the problem with learning C is that you need to learn stuff like make, autoconf, how the compiler + preprocessors work (what do all those flags even mea
by TheAceOfHearts 11y ago
I think the problem with learning C is that you need to learn stuff like make, autoconf, how the compiler + preprocessors work (what do all those flags even mean!?), how making "cross-platform" stuff works, how to pull in and use "libraries", C-isms, how to test, etc. C itself is a very small and simple language, but the tooling and patterns are old and mysterious.
In college I learned how to program embedded systems with C (and ASM). Once I knew how to debug, build, and push it onto the device, it was surprisingly easy and beautiful. There's no confusing and magical abstractions, it's just you and the hardware. You pull up the microcontroller's manual, and as long as you have an idea of what the lingo means, you can make it dance to your will.
Something I disliked was that with both microcontrollers I used, there was dark magic involved in building and uploading your binary. One of the devices provided examples for a specific IDE, so I imported that example and used it as a base. I could keep modifying it and keep adding files, but I never managed to setup a "new" project. The other device was similar, but instead of using an IDE it provided a Makefile, which was nicer.
Does anyone know any good resources on learning and writing real-world C? I've looked at C projects here and there over the years, with the most interesting being GNU Coreutils... But even if I can eventually understand what some code does, how do I learn why it does it that way?
I'd love for a guide that showed things like: "do X and Y because of this and that", "test X and Y by running the following", "write tests using XYZ", "debug X using Tool A, and Y using Tool B", "pull in this library by copying these files into these places, and use it by adding the following lines to the following files", "generate docs by pulling in this tool and set it up by doing the following", etc.
In the web world there's a massive set of problems, but it tends to be easy to find "boilerplate" generators that will get you up and running. And after you've tried out a few of them, you can usually pick up how people are mixing and matching different tools.
EDIT: Another comment linked to "Learn C The Hard Way" [0], and after browsing through the chapters it seems to cover a lot of the topics I'm interested in.
[0] http://c.learncodethehardway.org/book/ http://c.learncodethehardway.org/book/
- WalterBright 11y agoI never found C's transition from source code to hardware to be confusing. I struggle to comprehend why so many people, some longtime professional programmers, have trouble understanding what a linker does. On the other hand, I downloaded the CUDA SDK. I couldn't even figure out where the GPU compiled code even resided. I suppose I was just supposed to take it as it "just works" (and it did), but it all left me highly uncomfortable.
- yoklov 11y agoProbably in memory? Most GPUs use different ISAs, and they're mostly proprietary, so they usually have to be compiled (at least partially) at runtime. Then again, never used CUDA so who knows
- iamcreasy 11y agoAMD provides their specification. I guess up to HD4000 series.
- TheAceOfHearts 11y agoThe OS is big and scary. When you're working directly with the metal, it's SUPER simple, there's no magical abstractions. But when you're using libraries and doing system calls you don't really know what's going on. Sure, you can dig into em sometimes, but it's more than a bit daunting.
- kibibu 11y agoJavascript has the same problem, but I think its worse. At least there are platform standards in C (autotools in GNU, msbuild on Win). Trying to figure out how Grunt/Gulp/Broccoli, LESS/SASS/Stylus/Jade, Coffeescript, Uglify, Bower, Browserify, Require.js, AMD/CommonJS, NPM etc all work together is a nightmare. It's all too hard, so people added Yeoman, Brunch, or other things to generate application configs - but now you need to decide which skeleton/generator you want, which takes you down the JS and CSS framework rabbithole. Of course the only sane response to all this is to write another build system that doesn't repeat everybody else's mistakes. In comparison, Makefiles are remarkably usable.
- voltagex_ 11y agoYep. I'm more comfortable with Make than I am with JSPM/NPM and System.js. The process to minify my javascript and CSS and then replace the paths in the HTML so my pages actually work seems to be needlessly complex. I'm seriously considering whether I can use Make for my production website builds - the only issue is Windows support.
- Gibbon1 11y agoYou have a couple of options with make under windows. There are native versions of make. Where you are using dos shell commands to do stuff. MinGW provides Posix compatible native commands. (you can use ls, cat, etc commands). You can use msys which gives you a Posix compatible build env. Finally something like Cigwin provides both a Posix compatible build env and Posix compatible runtime as well.
- TheAceOfHearts 11y agoTry out webpack. Webpack makes handling all of your assets into something trivial. I converted around 3,000 lines of Gulp files into a 200 line webpack config. (Most of which ended up being alises, and this is a reasonably big project.) For an example of what makes it so awesome: var img = require('./foo.png') // "/output-path/0dcbbaa701328a3c262cfd45869e351f.png" Webpack will copy this file (foo.png) to your output folder, and rename it using the file hash, so it does cache-busting. You don't need to use other build tools, you can just use the webpack CLI. I personally use npm scripts. Webpack also allows you to setup aliases for modules, as well as load pretty much anything you can imagine. You wanna pull in a module that doesn't use CommonJS and instead exports a global? Webpack has global-loader. AMD is supported as well. Oh, and this includes a sane development environment, with reload on save, as well as hot loading assets that support it. (Check out react-hot-loader: http://gaearon.github.io/react-hot-loader/ http://gaearon.github.io/react-hot-loader/, but it works with css as well.) And you can require css files in your components, and then add the extract-text-webpack-plugin so it'll rip the css from the generated JS bundle! Aaaaand it handles SourceMaps, so you don't have to worry about some plugins (looking at you gulp) not playing well together. Finally, it also handles minification, either through a CLI option or in the config.
- thelogos 11y agoPersonally, the hardest part for me was keeping track of the size of everything. Coming from a higher level language, keeping track of the bits and bytes takes some getting used to. Working with arrays in C is much tougher especially when the compiler will compile almost anything you give it, and even a small mistake is catastrophic. Before C, I was pampered and took everything for granted. Now I appreciated my life more after C and feel blessed every time my IDE gives me a warning.
- fr0styMatt2 11y agoMy pet hate is #include files. The whole way that C handles multiple source files just seems archaic to me, having worked in higher-level languages. I wish C had a proper package system that was standard, so I don't have to mess around with things like include file path order (or my favorite, the C++ template definitions having to be in the header files thing I only recently learned about).
- NhanH 11y agoInterestingly, I first started with C (although I haven't written a line of C code for a long time), and when I first move to higher-level languages, I dislike the fact that I have no idea where the file I just imported is. Moreso when I'm playing with obscure/ new language: if I can just import whatever files I wanted (rather than at package level), it seems that would be much easier to hack on the language/std itself.
- pjc50 11y agoI have no idea where the file I just imported is A sufficiently long include path can give you this problem anyway. I recently tripped over this when I created a "reason.h" and discovered that Windows had a file of the same name deep inside MFC.
- fao_ 11y agoI recently tripped over this when I created a "reason.h" and discovered that Windows had a file of the same name deep inside MFC. It is my understanding that that would be solved by placing your file in a local directory called 'inc/' and having: #include "inc/reason.h" instead of: #include <reason.h> (or just replacing "" with <>). But I have never programmed in VSC before, so I may be wrong.
- userbinator 11y agoI don't think you need to learn the build system until after you're comfortable with single-file programs, where "gcc yourfile.c" is enough. Then add compiler options, and get to know Makefiles after your programs grow large enough to require multiple files. GNU coreutils (and in general, a lot of the GNU projects) are rather excessively complex and certainly not what I'd advocate "learning by example" from. Take a look at the BSDs' standard utilities for simpler, more straightforward code. But even if I can eventually understand what some code does, how do I learn why it does it that way? I believe that the best way to learn "why" is to ask "why not". You will see that a lot of programmers, IMHO unfortunately really don't know why and are just doing what they were taught to. If you don't do X, then either [1] it doesn't matter and you don't actually need to, or [2] it does and you realise the reason why, when you see how X makes things simpler/more efficient for either the programmer or the machine, or both.
- yoklov 11y agoDefinitely. Not only is it worth doing the wrong thing first to understand why its wrong, its also often worth revisiting after you have more experience with the other alternatives. Especially when it comes to programming paradigms and stuff like 'best practices'. Theres a lot of cargo-culting in programming culture, and you really shouldn't take it as dogma.
- rhodysurf 11y agoI switched to CMake for all of my C and C++ projects and i never looked back. Takes care of a bunch of makefile issues for you once you get used to it.
- ndesaulniers 11y agoMy recommendations (I've read a lot of C books), I think you can get away with just these two: * Head First C - David Griffiths * Expert C Programming: Deep C Secrets - Peter Van Der Linden
- iyn 11y agoThanks for recommendations. BTW: just want to say that you have a fascinating blog! I've just spent last hour only skimming through some of the articles and bookmarking them for later. (Link for the lazy: https://nickdesaulniers.github.io/ https://nickdesaulniers.github.io/)
- ndesaulniers 11y agoI wrote it for readers like you!
- pjc50 11y agoI reccomend "Programming in the UNIX Environment" by Kernighan and Pike. Partly because it was written so soon after UNIX and C themselves, it has very little of the modern 'cruft' in it. It's at the level of "cc program.c -o program". Make is fairly easy to learn, at least in its basic form. Autoconf is horrendous.
- charlesL 11y ago> Make is fairly easy to learn, at least in its basic form. Autoconf is horrendous. I completely agree. Make is extremely flexible on it's own. I don't understand the need to abstract the build system to generate thousand-line makefiles that are impossible to hand edit.
- benwaffle 11y agoThe idea is to make it work on all platforms, only requiring minimal POSIX compatibility. It also checks for any requirements you specify, and sets up stuff like make install, make dist-check, make check. It handles compiling your code into a library regardless of the os
- pjc50 11y agoAs benwaffle says in adjacent comment, make is the 80% solution that works most of the time. Autoconf is the 100% solution that's supposed to work everywhere, no matter how weird or long-dead your UNIX is. In order to do that it does a vast number of compatibility tests. The result is complex enough that simple substitution of makefiles doesn't quite cut it. Of course, that imposes the cost of 100% compatibility on every developer, when most would be happy to just build on today's Linux and call it a day.
- clarry 11y ago> Autoconf is the 100% solution that's supposed to work everywhere > Of course, that imposes the cost of 100% compatibility on every developer, when most would be happy to just build on today's Linux and call it a day. The reality is that when you use programs built with autotools on systems that aren't mainstream, you'll have troubles. Because the scripts aren't right and were only tested by Linux developers on Linux. And much of the time, I find it faster and easier to fix a broken makefile than to fix broken autohell.
- charlesL 11y agoFrankly, I don't use a lot of crazy libs or IDEs when writing C code. Most of my projects consist of one or two external libs and a few simple makefiles. I use Vim and clang/gcc for compiling and lldb/gdb for debugging. As for compiler flags, the only ones I ever worry about are `-O`, `-g`, `-c`, CFLAGS and LDFLAGS. What I've learned is that the way C includes other files/libs is extremely simple. The header files are simply a simple mapping of the code in the `.so` or `.a`. If it's a n `.so`, you can't make it a static executable and if it's `.a` you have to. I've never worked directly with low-level microcontroller programming, and the extent of my electronics knowledge is some fiddling with arduino. When I did that I used ino (http://inotool.org/ http://inotool.org/) to compile and upload code to the board. EDIT: Projects that have good C code include: http://suckless.org/ http://suckless.org/ and the linux kernel (and other stuff by Linus Torvalds). Avoid anything with GNU (most of it's over-engineered)
- bnolsen 11y agoAdding -Wall would be a very good addition that would help with fixing a few things as well.
- benwaffle 11y ago21st century c http://shop.oreilly.com/product/0636920033677.do http://shop.oreilly.com/product/0636920033677.do
- Flimm 11y agoI agree. I think tools like Valgrind and GCC debugger should really be introduced to the beginner from the get-go.
- bnolsen 11y agounit tests are more effective than using a debugger. I use a debugger a couple times a month and unit tests with sprinkled asserts and debug prints in the code.