6 ms·
I've been programming in Python for a long time and recently took up an OS class which exclusively used C. Syntactic differences aside (declarations in C can ge
by krat0sprakhar 10y ago
I've been programming in Python for a long time and recently took up an OS class which exclusively used C. Syntactic differences aside (declarations in C can get pretty hairy), the steepest learning curve while writing anything useful in C is with a) pointers and b) memory management which this guide doesn't seem to cover.
From my experience, the best way for learning C has been [0] Build Your Own Lisp and Zed Shaw's [1] Learn C The Hard Way.
That and of course spending countless hours debugging segfaults and memory leaks.
[0] - http://www.buildyourownlisp.com/ http://www.buildyourownlisp.com/
[1] - https://learncodethehardway.org/c/ https://learncodethehardway.org/c/
- greenshackle2 10y agoI enjoyed K&R C, but I know it's a bit outdated - I was thinking of picking up a more modern C book. Build Your Own Lisp implicitly claims you don't need to know Lisp to learn from the book: > We will be covering many new concepts, and essentially learning two new programming languages at once. Do you think that true? Did you know Lisp before reading it? (I've done the first half or so of SICP so I know some Lisp.) Right now I'm working on designing and implementing a virtual 16 bit CPU and an assembler, I guess the logical next step would be an OS class, then something like Build Your Own Lisp. (For context, I write Python applications at work, but the work done by our embedded system C guys seems more interesting; I'm trying to learn enough C that I could be useful on one of their projects.)
- krat0sprakhar 10y agoYou will end up learning only that subset of lisp that this book implements. So this book is definitely as not overwhelming as it sounds. In my case, I did know some lisp (Clojure) before starting out, but I'd strongly recommend that you don't hold back based only on that requirement.
- sn9 10y agoA slightly more modern book that's a really excellent book to follow K&R is Hanson's C Interfaces and Implementations. It's a book written in a literate programming style that describes how to build a flexible and modular library of data structures.
- protomikron 10y agoIMHO pointers are mostly hard at the beginning because the asterisk-operator is overloaded: On one hand it declares (and optionally initializes) a pointer (type * x = ...;) and on the other it dereferences it (type y = * x;). At least for me that was the thing that puzzled me the most. I would prefer something like that: // declare and initialize x @uint32_t x = 42; // dereference x uint32_t y = ~x; I.e. have different symbols for the type and the dereferencer ('@' and '~' as an example). //edit: thanks for the help, fixed snippets - HN's parser does not get pointers either apparently :)
- greenshackle2 10y agoI was about to tell you to escape it but I actually cant find a way to print an asterisk on HN EDIT: you can print one by leaving a space after it: * aa
- protomikron 10y agoYeah thx, I just realized that myself.
- jstimpfle 10y agoDo it like * this
- jpindar 10y agoI agree, the notation was the hardest part of learning about pointers. Having a 'cheat sheet' - a program with examples of common ways of using pointers - helped me quite a bit.
- mixmastamyk 10y agoI first learned pointers with Pascal and believe it was simpler with the ^ operator, but it has been so long I don't remember.
- e12e 10y agoFor some reason I also found pointer/references easy in Pascal, but struggle with them in C. I also find it easier to work with direct/indirect addressing in assembler than in C. I'm not entirely sure why - seemingly the differences between C and Pascal syntax and behavior for references seem trivial -- and yet somehow in Pascal it feels intuitive, while in C it feels complicated.
- appleiigs 10y agoIn your opinion, did learning C make you a better programmer in Python?
- roel_v 10y agoJust knowing C probably not, but knowing how PHP is implemented internally did make me write PHP differently; and I suspect the same would be true for any scripting language. (for example being much more conscious about pass by reference, the cost of various functions, the overhead of calling functions and doing data conversions etc).
- eveningcoffee 10y agoIt covers, but very slightly (see below, I skimmed it exactly to look for it). My observation is that this is really hard for many people. I helped couple of my course mates with background mainly in Python and this was the only thing they struggled with, and they struggled hard. It forces you to be very observant and conscious about the situation. Until you get that far, you will continue to suffer. C is really unforgiving. Every once in a while, you'll see a C program crash, with a message like “segmentation fault” or "bus error.” It won't helpfully include any indication of what part of the program is at fault: all you get is those those two words. Such errors usually mean that the program attempted to access an invalid memory location. This may indicate an attempt to access an invalid array index, but typically the index needs to be pretty far out of bounds for this to occur. (It often instead indicates an attempt to reference an uninitialized pointer or a NULL pointer, which we'll discuss later.)"
- protomikron 10y agoAs always, I recommend valgrind. $ gcc -Wall -g prog.c -o prog $ valgrind ./prog ... ==25494== Process terminating with default action of signal 11 (SIGSEGV) ==25494== Access not within mapped region at address 0x0 ==25494== at 0x400532: main (prog.c:5) ==25494== If you believe this happened [...] ... $ This helps a lot while debugging. Note that it is also helpful when debugging memory leaks and can give you the exact line in your code where you issued alloc() that you did not free later.
- eveningcoffee 10y agoVery cool, this would help a lot indeed. Are there other such tools a casual C programmer should be aware of?
- protomikron 10y agoYes, there are gazillions of useful tools, but another important point is that C is tightly coupled to *nix systems and the best platform to develop C is definitely a Linux-based system (ignoring embedded systems for the meantime, where C also shines), as otherwise most quality information that you find in the interwebs won't work for you.
- sigjuice 10y agoBoth of these are listed under "Stuff that should be avoided" here http://www.iso-9899.info/wiki/Main_Page#Stuff_that_should_be_avoided http://www.iso-9899.info/wiki/Main_Page#Stuff_that_should_be...
- mataug 10y agoThis ^ Syntax and data structures are usually the easiest part of learning a new language. Leveraging implicit conventions and trying to build anything useful is much harder. This is still a useful reference for anyone looking to jump into C.
- marmaduke 10y agoUse the address sanitizer and valgrind on your tests and you'll keep your sanity.
- castratikron 10y agoThat's why you shouldn't put anything on the heap in C.
- Sir_Cmpwn 10y agoI wrote a blog post that goes over pointers in C that may be useful: https://drewdevault.com/2016/05/28/Understanding-pointers.html https://drewdevault.com/2016/05/28/Understanding-pointers.ht...
- userbinator 10y agoFrom my experience in teaching C beginners, I think declarations are one of those things in C that people either get frustrated by very early on and give up, only memorising a small set of fixed patterns for the common cases; or see the pure elegance of the system, once it's presented to them in the right way. The two things to keep in mind to become one in that second group are "declaration follows use", and the operator precedence (array and function-call are on the right and have higher precedence than pointer indirection on the left.) Declarations must not be parsed left-to-right; in the same way that 2 / 3 + 4 * 3 is not parsed left-to-right but as (2 / 3) + (4 * 3), int **foo[10]; is parsed as int *(*(foo[10])) . What "declaration follows use" means is that the ultimate type of the object is exactly as it says after all the operators are applied (in the correct order): when the array subscript is applied to foo, and then that object dereferenced twice, the type of the expression is int. Thus foo is an array of 10 pointers to pointer to int, foo[i] a pointer to pointer to int, * foo[i] a pointer to int, and * * foo[i] an int. http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html
- pjc50 10y agoIf the system were elegant it wouldn't be quite so wildly inconsistent between prefix and suffix notation. "Middle-out" is not elegant. I mean, I've been writing C for 20 years and would still prefer to break down a declaration with intermediate typedefs rather than write "pointer to function taking array of pointers to int-returning functions taking a void pointer, which returns a pointer to int-returning function taking a void pointer". If you can write that as a declaration right first time you can give yourself a serious pat on the back.
- wruza 10y agoChallenge accepted, no cheating, no references, just editor and me. Hope that I got it right: int (*(*p)(int (*[])(void *)))(void *); The process: ?which-returns (*p)( ?taking ) ? (*p)( int (*[])(void *) ) int (*(*p)( int (*[])(void *) ))(void *) Testing with cdecl.org [1]: declare p as pointer to function [taking] (array of pointer to function (pointer to void) returning int) [, which] returning pointer to function (pointer to void) returning int F-yeah. Can I has my pat? Seriously, it is not so hard if you used each component at least 100 times. That's few years of practice of non-hiding behind typedefs. You can't learn if you don't practice. (I'm not arguing that this should not be decomposed.) [1] http://cdecl.ridiculousfish.com/?q=int+%28*%28*p%29%28int+%28*%5B%5D%29%28void+*%29%29%29%28void+*%29 http://cdecl.ridiculousfish.com/?q=int+%28*%28*p%29%28int+%2...
- gigatexal 10y agoThanks for this. I've failed C courses in university and in MOOCs when things get to pointers.
- analog31 10y agoI understand pointers pretty well, having programmed them in TTL logic, assembly language, Pascal, and BASIC (via PEEK and POKE). Python is a relative newcomer to my tool chain. What confuses me about pointers in C is simply the syntax, and I'm sure it's because I just don't write enough C to be able to read it with any kind of fluency. I find C programs to be harder to read than any other language that I'm regularly in contact with. Of course this could be because of what C is used for.
- blueside 10y agoI love Zed but I couldn't recommend Learn C the Hard Way to a beginner. The book starts out good, but then greatly ramps up complexity around chapter 17 (heap and stack memory allocation) without a good introduction to many new concepts, explanations are just completely missing. Most people I know who've all tried to go through the book ultimately start to lose interest and give up there unfortunately.
- Iv 10y agoUncommon and slightly extreme viewpoint: the best way for a python programmer to learn C is to learn a bit of assembly first. Python is a high-level language: it provides tools to manipulate abstractions easily. C is a low-level one: it gives you access to chunks of memory (and far enough ropes to hang yourself while shooting yourself in the foot while drowning) Assembly is not hard to learn. It has a reputation of being hard because it is tedious to use and that optimizations can be very intricate but learning basic assembly does not take long. Learn about MOV, ADD, JMP, INT, a few conditional jumps, learn how negative numbers are represented, what a memory mapping is, what a program counter or a stack counter are. Then, go back to C. Read a bit about how function calls are made, memory allocated, and you will see that C is actually a high-level assembly language. All the hard parts of C will become obvious: a pointer is a variable that stores an address, a stack overflow, a segfault, a memory leak, all these will make sense very easily within that framework.
- vram22 10y ago>Uncommon and slightly extreme viewpoint: the best way for a python programmer to learn C is to learn a bit of assembly first. I agree. I also sort of learned it that way. The difference was that I learned Turbo Pascal first, before C, and BASIC before TP, but then either while learning TP or C, I also learned some assembly and something about the fundamentals of computer hardware and microprocessors (as related to programming, not electronics) in an interleaved manner. Was lucky to have access to some very good books on all these (and many other topics) from the British Council Library in the city I was in at the time. Gave me a solid grounding in many topics related to computers.
- xorxornop 10y agoCompletely agreed! In the past, I had always avoided C because I didn't understand why many aspects of why it is the way that it is (eg pointers etc). Then I took my university's CPU architecture courses. That sorted that fear right out since I had to go from the ground up, learning everything I'd previously largely avoided or ignored - everything from transistors and the basic logic gates (AND, OR, XOR, NOT, etc) they consist of to full adders, all the way up to pipelined CPUs. Naturally, we learned assembly as part of this. C made an awful lot more sense after all of this! I still don't like using it, but that's a lot more to do with my understanding of it's dangers (I prefer to let the compiler do the hard work of verifying my programs make sense, a la Rust &c), rather than a fear born of ignorance.
- metaphor 10y ago>> Syntactic differences aside (declarations in C can get pretty hairy), the steepest learning curve while writing anything useful in C is with a) pointers and b) memory management which this guide doesn't seem to cover. Coincidently, I was making small talk at the office today with an undergrad (EE major) intern who's unenthusiastically taking a course in C programming to satisfy requirements. I told her that although those two points you brought up were precisely why traditional CS types generally hate/avoid C, it's imperative that she grasp these concepts as soon as possible (along with picking up an HDL) if a career in hardware floats her boat. Coming from a formal hardware background myself--and ever so envious of the traditional CS types who were always hacking the wee hours away--I ended up picking up a used copy of Cormen and took some CS baby self-steps over a few semesters, using both Facebook Puzzles[1] and Project Euler[2] as a condom so it'd be much harder to become impregnated with my own bullshit. And yet, to this day, I still feel like a bag of suckass compared to our resident graybeards; last time I hacked some C was only a few months ago to make a JEDEC[3] interpreter that metaprograms source in an obscure, obsolete proprietary language, but I'm admittedly ashamed to share the source with anyone out of fear of affirmation that I still suck hind tit at C after all these years. >< [1] https://web.archive.org/web/20091130184215/http://www.facebook.com/careers/puzzles.php https://web.archive.org/web/20091130184215/http://www.facebo... [2] https://projecteuler.net/ https://projecteuler.net/ [3] https://www.jedec.org/standards-documents/docs/jesd-3-c https://www.jedec.org/standards-documents/docs/jesd-3-c
- deleted 10y ago[deleted]
- fredmorcos 10y ago> That and of course spending countless hours debugging segfaults and memory leaks. Maybe that's because you think that: > the best way for learning C has been [0] Build Your Own Lisp and Zed Shaw's [1] Learn C The Hard Way.
- nnq 10y agoCare to be more specific about what you mean? What would be your preferred way of learning C? Really, how can you get proficient at programming C without spending countless hours debugging segfaults and memory leaks?!
- fredmorcos 10y agoThe resources you refer to are abysmal, criticism is available and widespread online if you want details. You get proficient at C programming by properly understanding what you learn rather than spend countless hours in trial and error cycles. There isn't much magic to it and the concepts are quite simple. In my opinion if you're looking to get up to speed quickly instead of carefully writing code then C is not for you.
- nnq 10y agoTo be honest I learned from K&R but I guess it's too outdated and not really about "modern C". But of those resources one of them seemed quite fun and probably harmless ([0]) and the other one ([1]) pretty practical and "bad style" but error-free as far as I read through it, with the added benefit of teaching newbs that tools matter a lot and getting them pretty fast into using make, valgrind etc.... In your opinion, what would be: 1. the modern equivalent of K&R? 2. a solid reference to keep around and consult afterwards? (with 1 and only 1 answer to the questions above, please, not a list, 'cause every time I search "learn c" I get over lists upon lists, of high quality comments - which is bad because you can't actually dismiss them as rubish, you actually have to read them -, that only throw you into paralysis by analysis, so you just say, "fuck it, just grab K&R or LCTHW, then grab a C open source project you feel like hacking on and start banging you head on it"...)
- SamUK96 10y agoSurprised you didn't try C++ if you found C quite difficult. C++ teaches memory management but without the insanity level dialed to 11 like with C (or more so with Assembly). I've always discouraged learning Python first. Python disconnects you so much from what is really going on and does so much for you that you don't develop algorithm and conceptual skills and computer knowledge. All the developers where I'm at, from SQL to Python, Node to Angular, everyone, all say the same thing about Python and other "hyper-package-assisted" languages.