top | item 4057564

A Boggling Return to C

162 points| ColinWright | 14 years ago |thraxil.org | reply

85 comments

order
[+] yason|14 years ago|reply
I started with assembly language and I thought C was heaven. Of course, there are easier languages, there are more powerful languages, there are fancier languages and whatnot. I like Python and I adore Lisp. But I still love C most.

C is the sweet spot where I can extend my programs to do high-level stuff while still keep my hands down on the actual hardware I'm programming. I like that a lot, probably because I grew up banging hardware. Or maybe it is because I like to keep in touch with the actual device that I program: bending some definitions, C is what my machine does. Even if I embed Lua and script parts of my program, I'm still conceptually working on a C runtime, complete with addresses, pointers, integers and registers. That's why I also like LLVM as it abstracts away different instruction sets into a generic high-level instruction set, much like C abstracts different assembly languages into a generic high-level assembly language.

C also has the property of being most enjoyable code to read. I've spent a lot of time just reading C out of sheer enjoyment. C is tricky: it can be a total mess or it can be terse and beautiful and clear. No matter what, it describes exactly what it does to my machine. Read some of D.J. Bernsteins source trees to get an idea of how neat C can be.

[+] X-Istence|14 years ago|reply
Reading DJB's code can be both awesome in that his code is written clearly and neatly, yet at the same time it can be an excercise in frustration because he has foregone most of the standard library and written his own (especially string management, which is superior, in my humble opinion), so it can look foreign or weird to people looking at it.

The source to qmail/daemontools is a pleasure to read though and having read it I feel like I have learned a lot from what it has to offer.

[+] xarien|14 years ago|reply
My intro course in college was run this way as well and to this day, I'm very glad to have started my career bottom up instead of top down...
[+] ralfd|14 years ago|reply
Nice try Mr. Daniel J. Bernstein.
[+] babarock|14 years ago|reply
I absolutely love writing C. Up until late 2011, it was by far the language I used the most. I am now learning Lisp (not exactly, I'm using Lisp in SICP), and use Python more than before.

One thing I realized, is that reading C is more tedious than code in other languages. Sure that's a gross generalization and is not true for every piece of code out there. However, I find I have less troubles picking up a Python project, understanding how it's written and start contributing than I have with C.

A few weeks ago, I was looking at the code of Qemu. The code relies heavily on preprocessor macros and some weird gcc-only syntax that made my head hurt. It was difficult.

I guess what I'm trying to say, my only problem with C is that it doesn't force the programmer to write in a clear understandable way. Or maybe that's just me.

[+] luriel|14 years ago|reply
> The code relies heavily on preprocessor macros and some weird gcc-only syntax that made my head hurt.

The preprocessor is generally recognized as one of C's biggest flaws, is not for nothing that Ken Thompson, the first C programmer and the greatest influence on the language other than DMR himself, cut down most of the preprocessor when he wrote his own set of C compilers for Plan 9: http://doc.cat-v.org/plan_9/4th_edition/papers/comp

And of course Go has no preprocessor.

As for your second complaint, one can't blame C for gcc's extensions ;)

You should try Go, many people would consider it C's spiritual successor (or as somebody put it: the language the people who created C would come up if they had 40 years to think about how to polish and improve it), it keeps all the simplicity of C, while being probably the most readable language I have used, it is concise but keeps everything explicit, and figuring what code does is very easy, because code does what it says and says what it does, no dark magic needed.

[+] pm215|14 years ago|reply
The problem with the C preprocessor is that it's sat in a "sour spot" where it's often possible to use it for a task but only by bending it so far that the result is pretty ugly and not very comprehensible. If the preprocessor were less powerful it would be obvious you needed to use a different tool; if it were more powerful it wouldn't result in ugly messes; but it's sat in the sour spot in the middle.

(Make is another tool which suffers from 'sour spot' syndrome IMHO.)

[+] X-Istence|14 years ago|reply
The one thing that I have trouble with in Python projects is that it can be very difficult to figure out where the actual object is from that is being imported.

  from somedir.somefile import objectX
Then when you go to somedir.somefile you find out objectX is nowhere to be found only to figure out later that it is dynamically created and added to that namespace and it can be imported in the file you've been reading because some init function was already called by the module "foo".

There are quite a few codebases where I have been hunting for the superclass for example so that I could see if it offered functionality I wanted or how it was structured so I could find out where some function was defined and what EXACTLY it did due to no documentation and it took me a while.

I love Python, don't get me wrong. It is by far one of my favourite programming languages, but sometimes it can be very non-obvious where something is coming from and how it is getting there. This may be more of an issue on a project to project basis, but it is an extra complexity that I have found can be rather annoying.

[+] andrewcooke|14 years ago|reply
maybe the biggest improvement in c programming over the last decade is valgrind, making purify-like debugging available to everyone. it's completely removed a whole class of annoyingly hard to fix bugs from my code.
[+] pjmlp|14 years ago|reply
Tools only created to solve the lack of safe constructs in C.

C made lots of sense in the context it was developed, but the world would be better if we had safer systems programming languages.

Valgrind, purify and friends are required to C, the same way Java requires IDEs, to improve language usability.

Have said this, the new trend in having static analysis tools integrated in the development process, like Clang, Eclipse CODA, Visual Studio's tools or HP Code Advisor, among others, can bring a bit more safety into C.

[+] emmelaich|14 years ago|reply
Absolutely. Also the gcc mudflap library, splint static checker, Torvald's sparse have really helped.

And that's without the commercial tools such as Coverity.

[+] WalterBright|14 years ago|reply
Valgrind has almost single handedly rescued C from the ashheap of history.
[+] heretohelp|14 years ago|reply
This is why Zed pushed it so hard in Learn C the Hard Way.

Some people actually had the gall to complain about him ensuring the noobies learned to use valgrind before proceeding to write any real code.

Ingrateful dipshits don't remember what the pre-valgrind/dtrace days were like.

[+] KonradKlause|14 years ago|reply
valgrind, is only available for a few platforms/architectures. If you are unable to writer proper C programs without valgrind's help, please go home. :-)
[+] DanielBMarkham|14 years ago|reply
I'd love to have time to play around with C again. I picked up C back in the late 80s when I became convinced that no matter what other programming languages I knew, I wasn't going to be a "true" professional unless I could sling code in C and C++. Fun times.

I also love the idea of using a trie here. That's something else I've been wanting to play around with for a while. Although now I'd do it in a functional language.

He brings up a good point: people coding in C tend to stop and think about data structures, memory usage, and clock cycles in a way people using higher languages very rarely do. It's part of the way to "think in C" Internal data structures are also much more important in FP. Interesting how different languages cause you to think in different ways. (Sapir-Worf anyone?) :)

[+] X-Istence|14 years ago|reply
I also think it really depends on how you are taught or where you start...

I started programming in C/C++ (my first book, I'll admit at age 9 was a C++ for Dummies book, it came with a compiler :P).

I have learned and use a lot of higher level languages, but I still think about data structures, I still think about what the best way would be for handling the data most efficiently, mainly because I don't want to rely on the language doing the right thing.

I've seen Java programmers though that then start programming in C++ or even C and never pick up the art of thinking about their data structures. I work with one co-worker now that went through the extreme trouble of implementing Java like enum's which have caused all kinds of "warts" and all kinds of issues because they are not enums and they aren't "real" classes.

Watching Java developers turn C++/C developers is really interesting, they bring all kinds of "bad" practices back with them and the code is worse off because of it!

[+] Xion|14 years ago|reply
"By way of comparison, the Python program takes 1.5 seconds to run, so that's about a 10X speedup."

Only tenfold? Interesting. While Python is surely not the slowest interpreted language around, a result like that borders on the performance of Java. That seems unlikely, especially given the fact that Python version uses worse algorithm.

I would think about how big is the portion of time eaten by I/O - that is, actually reading the `words` file from disk. I wouldn't be surprised if it eats most of the ~100ms that C needs to performs the task, leaving only a tiny percent for actual computation.

[+] thraxil|14 years ago|reply
Yeah, it was only a tenfold improvement because I was only benchmarking the solving of one board at a time, so a significant amount of time was spent on overhead, reading in the word list from disk, etc. I suspect if I ran multiple passes over larger boards, I'd see a much larger improvement. And the "worse algorithm" that the Python implementation used still wasn't that inefficient.

Ultimately though, this is still why Python gets used so much for real world work. Slow and inefficient as it might be compared to C, on real world problems where performance is dominated by disk seeks and network latency, it's good enough.

[+] shaggyfrog|14 years ago|reply
> a result like that borders on the performance of Java

I haven't seen the "Java is slow" chestnut in years.

[+] igouy|14 years ago|reply
>>a result like that borders on the performance of Java. That seems unlikely...<<

What Java program and timings are you looking at?

[+] fferen|14 years ago|reply
It's funny, because I recently tackled this exact same problem in Python as well. And guess what my first step was? Writing a prefix tree implementation, in order to use the exact same approach the author took in C. It never seriously occurred to me to do it any other way. It may be because I recently got into C as well after spending years with only Python, but honestly I think I would have done the same thing before that; that's just how I think. So I don't believe it's the language that dictates how much you think about efficiency, it's the programmer.
[+] rodelrod|14 years ago|reply
Well I didn't get back into C and last time I had to solve a similar problem typed:

  from Bio.trie import trie
[+] robryan|14 years ago|reply
Providing you are aware of prefix trees. I guess you could come up with the idea independently but I would assume a lot of people when presented with the problem wouldn't.
[+] thraxil|14 years ago|reply
Aha. Someone posted this up here. That's why I've gotten a huge burst of comments on the site in the last couple hours.
[+] dmansen|14 years ago|reply
I wrote the same program in Clojure somewhat recently. Boggle seems to be a popular problem. https://github.com/dmansen/boggle
[+] thraxil|14 years ago|reply
Nice. Implementing the same problem in Clojure has been on my todo list. Now I've seen yours though so I guess I'll have to think up another problem :)
[+] galactus|14 years ago|reply
The author implements an inneficient algorithm using python and then a better one using C. He seems to feel python is for dirty brute force and C is for "real" programming...
[+] thraxil|14 years ago|reply
Yes and no.

I wrote the Python version in probably under an hour. My girlfriend had gotten into playing some stupid Facebook version of Boggle and I just wanted to see her face when I came out of nowhere with implausibly high scores. I didn't think hard about the problem, just reached for the tool I know best and implemented the first obvious approach that came to mind. It worked as needed and I moved on. You make it sound like I think that's a bad thing.

Later, when I had a bit of time to think about it, it occurred to me that a trie would be a better approach, so when I was feeling like getting back into C and wanted a toy problem, re-implementing the boggle solver in C with a trie seemed like a good choice.

The experience of programming in the different languages does feel different though and I think it can affect how one approaches solving problems. Python is so good at just letting me solve the immediate problem that sometimes I rush and don't think things through or settle for a less than optimal solution. This will come back and bite me if that suboptimal code ends up getting built on and re-used elsewhere.

When I write C (or Go, Erlang, Haskell, etc. basically any language that requires me to think a little more up-front about how I'll implement it), I know going in that I'm going to be putting some serious time and effort into the code, so I tend to be more careful about things at every stage. The game changes from "get a result as quickly and painlessly as possible" to "write something that is elegant in itself". That's not always a win. Sometimes you are much better off building the prototype quickly, seeing flaws that you never would've thought of and then being able to approach the problem in a whole new way. Sometimes you just need a result quickly and time spent making things elegant or efficient actually is wasted (I'm not going to build a framework out of the boggle solving code anytime soon, eg).

I code in Python pretty much every day. I have for years. I probably will for years to come. It works for me. I'm just saying that sometimes other languages push you in different directions and I can see why, despite taking more lines of code to write, taking longer to write, having more potential for segfaults, and so on, languages like C still find a niche for writing systems and platforms. And that reason isn't just that it runs a little faster.

[+] abecedarius|14 years ago|reply
My Python answer http://stackoverflow.com/questions/746082/how-to-find-list-o... runs in 200ms on my laptop. It probably isn't actually within a factor of 2 of this C code, since I don't know what input board it was tested with to get 100ms, and it matters how much of the dictionary gets pruned while loading. I just stuck in a random 5x5 board and got 412ms real time, still pretty tolerable.

Edit: The next Python answer there uses a trie and takes 16.7 seconds on the 4x4 board. I like tries because they're elegant, but I hardly ever use them because the built-in collections are well-engineered even for problems you'd think are made for a trie.

[+] rohit89|14 years ago|reply
>> The C version is functional but will probably make more experienced C programmers cringe.

Oh boy. This looks like the type of C code I write. Could some experienced C programmer please point out what parts are cringe inducing ?

[+] finnh|14 years ago|reply
Writing a boggle solver was the first assignment in CS106X at Stanford, which was still taught in C when I took it in the fall of 1994.

The trie code as well as the display UI were provided - you only had to write the board-walking code.

Being as this was the first time I had ever written any program, I remember it being quite challenging but also really fun. It was great to see your own program utterly house you when you played against it.

I wonder if I still have that code somewhere ... it would be fun to look at / cringe.

[+] AlexFromBelgium|14 years ago|reply
I'm a student who learns C#, Java, Php, javascript... at school. I bought "C: A Refence Manual" to learn this summer...

Hope it will make me a better programmer.

[+] luriel|14 years ago|reply
If you want to learn C, K&R is the right way to do it.

C: A Reference Manual is excellent and is probably the only other C book you need, but only if you are already a C programmer, and only for what the title implies: reference.

[+] berntb|14 years ago|reply
I loved my Harbinson/Steele C book, a long time ago. Google says the last version came 2002 with additions on the web. Good memories, I'll buy a copy and read just for nostalgia. Thanks.
[+] programatico|14 years ago|reply
Did anybody noticed eC language www.ecere.com. Still under early development, documentation incomplete. Some issues with preprocesor and other stuff fixed. Definitely has potential.
[+] Roybatty|14 years ago|reply
<butthead>Uhh...hehe..uhh..hehe...he said boxen</butthead>