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

The thing I love about C is that it the most obedient of all languages and never assumes you are an idiot. Never does it tell me I'm about to blow my own foot off - it just does it and I've learned from the experience. Due to this it taught me how to think about things first and know of all the consequences of doing everything.

*NIX is the same.



Doing exactly what you told it is fine until you hit one of the many undefined behaviours and then it does exactly what you thought you told it only some of the time.

There's a lot of undefined behaviours to know about, and not all of them are obvious. You can be merrily compiling your programs fine on GCC and Clang and your intended behaviour seem entirely obvious, only to have it blow up on another compiler or with some different compiler flags.

That said, the undefined behaviours do allow the compilers to do some very tight optimisations, so swings and roundabouts.


That tends to matter less if you have a decent test harness and plenty of assertions!


Not everywhere C's undefinededness lies; e.g. shifting more sizof(int) bits - in case the shift parameter is dependent on your input.

"Decent test harness" is like "sufficiently smart compiler". Everyone assumes it will be there when planning, but in practice it is only available in very specific and not-generally-useful cases.


Here's a one-file, ISC-licensed test framework I wrote for ANSI C: "greatest" (https://github.com/silentbicycle/greatest). It doesn't depend on any dynamic allocation, or anything beyond C89, and compiles with zero warnings under -Wall -pedantic.


Hadn't thought about that. I haven't done much C since I discovered proper automated tests.


The following program compiles without warnings using gcc 4.7.0. The compiler uses the undefined behavior of accessing a variable that may not have been initialized to turn the conditional into one that's always taken, and then folds it away.

https://gist.github.com/2910012


valgrind:

   ==46592== Conditional jump or move depends on uninitialised value(s)
   ==46592==    at 0x100000E90: foo (in ./a.out)
   ==46592==    by 0x100000ED3: main (in ./a.out)
The matter is, valgrind exists. Everything is a C deficiency that valgrind is able to overcome decently is no longer a C deficiency practically speaking.


Not true. Valgrind is a dynamic analysis, while these kinds of errors could be caught by static analyses.


+1 I run valgrind on all my test cases.


clang does catch this:

  clang -Weverything uninit.c

  uninit.c:3:6: warning: no previous prototype for function 'foo' [-Wmissing-prototypes]
  void foo(int bar) {
     ^
  uninit.c:10:6: warning: variable 'lol' may be uninitialized when used here [-Wconditional-uninitialized]
        if (lol == 7) {
            ^~~
  uninit.c:4:9: note: initialize the variable 'lol' to silence this warning
        int lol;
               ^
                = 0
  uninit.c:37:3: warning: no newline at end of file [-pedantic,-Wnewline-eof]
  */
  ^

  3 warnings generated.


Glad to hear it--I've been looking into using clang more... This might just tip me towards it.


Interesting. Our compiler catches it, though:

  $ cparser -O3 -Wall -Wextra -pedantic test2.c -o uninit
  warning: ignoring gcc option '-pedantic'
  test2.c:3:6: warning: no previous declaration for 'void foo(int)' [-Wmissing-declarations]
  1 warning(s)
  test2.c:4:6: warning: 'variable lol' might be used uninitialized [-Wuninitialized]
Looking at the GCC bugtracker, there seem to be various warnings missing. http://gcc.gnu.org/bugzilla/buglist.cgi?quicksearch=uninitia...


There is something wonderful about low level programming langauges. I enjoy writing code and knowing exactly what the ASM is going to look like.

Python is fun, but I've got no idea how it is doing what it does.


This is very true but in my experience it's not always a good thing. It often leads me to premature optimization for instance.


I've been working on some ideas about how different languages lead to different sorts of premature optimization problems. I see a lot of OOP problems where developers try to avoid re-initializing or re-instantiating an object, and end up with weird partial-initialization or wrong initialization edge cases. I think language structure encourages this, and have been thinking about what other sorts of premature-optimization problems languages encourage.


This is exactly why I'm learning the various ASM right now. I am not content (depending on what I'm trying to do, of course) with a program just doing "something" that works.


How often do you need to know what it's doing?


Usually when something doesn't go as quick as you were hoping, profiling is unhelpful and you have to take the python bytecode to bits to find out what the hell it's doing inside.

That happens quite a bit these days for me.


Oh, and this. For sure. Why is everything so slow?!


> Why is everything so slow?!

Urf tell me about it. Does my flipping head in.


I used to play around with 3D graphics in real-time on the 486. I needed to know exactly what was going on to even get 30 fps.

I managed to get one of Michael Abrash's (ID software)loops to run 0.5 clock cycles faster, which I was pretty proud of.


When you are attempting to do a very specific thing. Not going to go into what that might be, unless you're curious, but sometimes you do need to know exactly or as close to exactly how your code is operating.




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

Search: