Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Learn C in Y Minutes (learnxinyminutes.com)
50 points by optimalsolver on April 13, 2021 | hide | past | favorite | 77 comments


If you want to write applications in C, do yourself a favor and read 21st Century C by Klemens.


By the way, IMHO the value of a language tutorial which omits the subject of safe Unicode strings is questionable. The tutorial by the link suggests using zero-terminated byte arrays - nobody in their sane mind should be doing so.


I also strongly suggest the C Programming FAQ:

http://c-faq.com/


Was expecting some snarky "Should I use C? No." single page but this was pleasantly well done.


"C, a language that combines all the elegance and power of assembly language with all the readability and maintainability of assembly language "


This guide recommends Learn C The Hard Way by Zed Shaw as further reading, but the ##C Freenode Channel specifically recommends avoiding it[1]. Can anyone tell me why this book is so contentious?

[1] http://www.iso-9899.info/wiki/Books



> Ah, C. Still the language of modern high-performance computing.

Yet its major compilers are written in C++ nowadays, and C++ is the main language for GPGPU computing, including the memory model used by NVidia on their cards.

I suggest some benchmarks updates to the author.


CUDA kernels look very much like C, you cannot use the STL and a bunch of other things.

For practical purposes, it is C.



To be fair, C++ just brings over templated wrappers around battle-tested C CUDA libraries like cuDNN, cuBLAS, etc.


CppCon 2019: Olivier Giroux “The One-Decade Task: Putting std::atomic in CUDA.”

https://www.youtube.com/watch?v=VogqOscJYvk

Future of ISO and CUDA C++

https://developer.nvidia.com/gtc/2020/video/cwe21284-vid

Thrust, the CUDA C++ template library

https://docs.nvidia.com/cuda/thrust/index.html

Optix, single-ray shader programming model using C++, and ray Tracing acceleration using RT Cores.

https://developer.nvidia.com/optix

Phew, so many wrappers yet to list.


CUDA has stopped being C in version 3.0, update yourself.


The core language is something a person might plausibly learn in Y minutes. Getting any useful work done almost invariably requires learning a lot about the environment and system your code is running in. The idioms of the Windows filesystem stack and most Linux userland code are radically different.

Being able to write code for one will not confer immediate productivity in the other.


> Constants are written in all-caps out of convention, not requirement

I wonder if there even is a language where something like this IS required. Most of the languages I know are case-sensitive yet the case doesn't mean anything specific in any of them, the case seemingly always is a matter of style.


AFAIK Go uses this for symbol visibility (e.g. symbols starting with an upper-case letter are exported, lower-case are private symbols).


>// Must declare a 'function prototype' before main() when >functions occur after >// your main() function.

This should have been formulated as:

You must declare a function as a prototype if you use the function before its definition appears in the code.

It's not just main but all functions.


As a web developer, who mostly spends time with Go and Python, is there any benefits of learning C?


Writing correct C is REALLY hard and slow (strings, manual memory management, concurrency pitfalls). So the investment is not paying off unless you are writing highly performance critical backend services.

What can pay-off is learning to read C. Node.js, Firefox, Postgres, Linux are all written in C/C++ so if you ever want to really understand what's going on or have to debug a nasty bug, then you will need to be able to find your way around these code-bases.


Also beyond the codebases themselves, interactions with the running code via GDB or tracing frameworks like dtrace, bpftrace, where you often reference C data structures. As well as being able to more fully understand the output of strace and other tools.


[flagged]


Most people do that on Arduino which is as c++ as it gets, with templates, etc...


If you mean hobbyists, then probably yes. But there are whole industries who use embedded C as the defacto standard, such as automotive.


I've worked on automotive and that was definitely C++, to a very deep level


Not really, but it depends. For instance, C is pretty great for compiling simple logic into WASM modules because the size overhead is minimal compared to most higher-level languages, or even C++ with careless use of the stdlib (and the missing memory safety isn't such a critical problem because everything runs in the WASM sandbox anyway).

E.g. I think the size of my WASM demos is quite reasonable for what they do:

https://floooh.github.io/tiny8bit/

https://floooh.github.io/sokol-html5/

However, AssemblyScript is also a pretty good choice for this type of stuff from what I'm hearing (while for instance languages with a complex runtime like C# are not).


I find that there is a lot of value in learning "how the sausage is made". Implementing your own baby version of the things you take for granted in higher-level languages is an interesting exercise — e.g. things like dynamic dispatch.

These are things that are not directly useful, but help you have a better mental model of how your language of choice actually works.


C is a low level language that compiles down directly to assembly. It is similar to Go in this regard, but is a bit lower level. You have to manage memory yourself and pointers in C are much more dangerous. Reasons to learn C would include: absolute performance needed maybe for say a Python C extension, kernel hacking, some other project you want to contribute to uses C.

I think it’s good to at least play with C and understand how memory management and pointers in C work, but I don’t think I would recommend you go out of your way to learn C or spend any significant amount of time writing it. At the end of the day it’s just another tool in the toolbox. I would recommend the book Code, by Petzold, if you want to really want to explore computers at a low level in a friendly format.


nitpick. I think C compiles directly to machine code. There's no intermediate step of compiling to assembly first.


> language that compiles down directly to assembly

Directly to machine code, not assembly.


If you're interested in:

-Embedded/cyber-physical systems software (firmware dev)

-Driver development (applies to Win/macOS/UNIX/iOS/Android)

-Contributing to the Linux kernel/OS dev in general

-Contributing to many large OSS projects (Apache, ffmpeg)

-Exploit/malware dev/reverse engineering

-Emulator development

you'd need to learn C.


Your question is too vague. Are there benefits in general? Of course, every language has something to offer.

Is it useful as a web developer? Only if you want to write extensions to Python in C or step away from the web developer label and learn systems programming.


For you, yes: you will better understand Python's source code, which is written in C. Poke around the source code a bit just out of curiosity after learning a bit of C and it becomes a bit interesting.


Oh man, multiple declaration on the single of code is nasty. Passing this kind of advice causes the beginners to develop bad habits.

At least these are not uninitialised variables.

  // Shorthands for multiple declarations:
  int i1 = 1, i2 = 2;
  float f1 = 1.0, f2 = 2.0;

  int b, c;
  b = c = 0;
Edited:.. After 1hr of posting my original comment, I noticed that there many opinions here.

I should have added why I think this is bad. Readability and maintenance. I would rather it have one line per declaration like the following..

   int i1=2;
   int i2=2;
Each variable easier to be changed, and reading it is clearer as there are no ambiguity to its type.

A common error I have see from "the multiple declarations in the single line" construct is that changing the type impacts all the variables whereas the intention was only for the first variable.


Just don't. This kind of prescriptive nonsense isn't nearly as helpful as you think it is. People value very different things when reading code, and vertical whitespace is not nearly the universal good you seem to think it is. For myself, I much prefer to trade slightly more complicated intra-line semantics for the ability to see more of the code on the screen at one time.

C has been around long enough to develop multiple competing paradigms for how to format source code, each with copious existence proofs that "Good Code" can be written in them. Conform to what you're maintaining, don't start fights.


This is also bad advice for C:

   void function_1();
Most people coming from other languages (like C++) will probably think that this is identical with "void function_1(void)", but without the explicit void argument list, the compiler will not warn if the function is accidentally called with parameters.


-Wstrict-prototypes

"Warn if a function is declared or defined without specifying the argument types."


This isn't the case nowadays.


Huh? Of course it is:

https://godbolt.org/z/8qYeK89rh

It's only an error in C++, but not in C, and the warning for this isn't included in the usual "-Wall -Wextra" set.


What is the “correct” version of this?


There is nothing incorrect about multiple declarations, but the syntax can become confusing when declaring pointers or arrays:

    char *s = "foo", *t = "bar";
A beginner might forget to add the asterisk in `*t` (especially if the first declaration is written `char*` with no space on the left of the asterisk). Similarly with multiple types:

    char *p = "foo", **q = &p, *z[2] = {"foo", "bar"}, *(*f)(const char*) = strdup;
This is correct and an intermediate C programmer won't have too much trouble parsing these, but in the interest of clarity, it makes sense to put each declaration on a separate line.

If you find yourself needing a lot of local variables, that's often a bad sign anyway, since it implies that the function has a lot of state to manage. In that case, you might want to rethink your implementation to make it less complex, or restructure the code to make it more managable.


Just split them into separate variable definitions:

    int i1 = 1;
    int i2 = 2;
The "usual footgun" is:

    int i1, i2, i3 = 0;
Only i3 will be initialized in this case. And since one needs to write out the assignments anyway, adding the type for each variable doesn't hurt either.

(today's compilers usually have uninitialized-warnings for this though, but sometimes people forget to add a -Wall to their compiler options, especially beginners, and then get stuck on such trivial problems).


Sorry

I

can't

read

your

comment,

could

you

split

your

words

into

separate

paragraphs?


There is nothing wrong with multiple declarations.


It's more difficult to read and edit.


I wish we could talk about style in objective terms, not just opinion. Opinions are kinda stupid. Here's my opinion: https://news.ycombinator.com/item?id=26790701 (actually that's just a faux opinion to make a point, but you get the point)

And if there are no objective facts, then maybe we could just stop calling other peoples' preferred style nasty and "bad habits" as if it were objective.


More difficult to read, more difficult to edit, but the worst thing is how difficult it makes diffing code. You should try using some kind of code versioning, git is a popular choice.


> More difficult to read

Subjective, and I do not find it more difficult to read. On the contrary, I often find shorter and denser expressions (especially around obligatory fluff that doesn't actually do much) to improve readability and make it easier to focus on the interesting part.

> more difficult to edit

Subjective, and I do not find it more difficult to edit.

In general, as long as you're not using a magnetized needle as your editor, creating new declarations or moving declarators around shouldn't be a challenge. It is trivial.

Nevertheless, if we want to consider the relative difficulty of various trivial edits, it can go both ways as far as multiple declarators per line are concerned. It can be easier to edit when you have a set of related variables and you need to change the type once. If you split them on a bunch of lines, you need to remember to change every line, which is more work and larger diffs. Oh the horror...

> but the worst thing is how difficult it makes diffing code.

I agree that it can make diffs a little harder to read but I have not found that to be a big issue in practice (having worked on both styles of code professionally and as a hobby for more than two decades).

If diffs were such a major concern, then we probably should adopt an assembly-style syntax with one instruction per line and no nested indentation. That would keep lines inherently short, so you don't have to locate and read the small change on a longer line. And you'd never have to pick small changes from a large hunk that is large mainly due to changed indentation. (At this point, I'd like to make a point about how splitting everything to more and more lines isn't always good for readability but I think I made that point already.)

By the way, you might wanna try out one of these modern diff tools that can hilight the actual change on a diff line.

> You should try using some kind of code versioning, git is a popular choice.

I can assure you that "multiple variables per declaration" have never been a problem for me with git. Coincidentally, the source code of git itself does have multiple declarations per line so looks like it wasn't a big problem for the authors of git either. (Obligatory example: https://github.com/git/git/blob/89b43f80a514aee58b662ad606e6...)


> with one instruction per line

I shouldn't be surprised, of course you're also the kind of person who writes terribly long lines of code. We have ultra-wide screens nowadays, better put them to good use.

Anyways, you might think you're making a point by repeating "it's all opinions", but that doesn't mean your single opinion is equal to everyone else. And on the subject of one vs multiple declarations, you're overwhelmingly in the minority.


For personal project my hard limit is 80 cols. For work stuff, mandated by employer, we do something like 120 cols with clang-format. Four or five columns of code side by side, so I can see hundreds of lines of code at once. I still prefer not to waste too much vertical space on fluff.

It doesn't sound like you understand the difference between block structured code with a function call syntax and assembly. Thanks for the attempted ad hominem btw ;-)

> Anyways, you might think you're making a point by repeating "it's all opinions anyways", but that doesn't mean your single opinion is equal to everyone else.

You keep missing the point. Did I say "that's just nasty and bad habits" about your preferred style? Nope!

All I'm saying is that it'd be nice if people stopped presenting their preference as objectively good style and telling that everything else is nasty and wrong. We don't need to share the same preferred color either.

The first comment in this thread would've been a lot more palatable if it just said "I don't like this style." That would also give the comment the right weight: it is just an opinion after all.

I don't think popularity of opinion should matter for discourse. Of course I know I'm in the minority, and that's why I think it's all that much more important to state my opinion. Or do you want to live in an echo chamber where only the popular opinion gets said and everything else is painted nasty and wrong and downvoted to hell? Well, that is pretty much what HN is today. I hope you're not contributing to that.


> you might think you're making a point by repeating "it's all opinions", but that doesn't mean your single opinion is equal to everyone else.

You're very wrong.

Readability is currently impossible to measure precisely. All the opinions are grounded in impossible to quantify and define personal skills, predispositions, and habits.

"It" may be "more readable" to you. And your friends. That is still just your (and your friends') opinion, describing a feeling you have. It's not measurable. It's not provable. It's just what you think is happening, but that's it - just a thought.

No facts. No measures. No definitions. As long as your opinion has these qualities, yes, it's equal to everyone else's.

The problem with that is that it's narrow-minded. You should try to understand that, in different circumstances and for different people, the feeling of what's readable and not readable can be drastically different from your own.

There are people who swear by ⍝P⌊ or K and say they're the most readable languages ever. Others will-tell-you-there's-nothing-wrong-with-kebab-case and that (parens (are (not (a) problem) either))))))). Some will tell you that any language will do, as long as you drown it in text (like Knuth's literate programming). Yet others will try to make prose be executable, like in Inform. Some will tell you $igil$ improve readability, some will say they don't. Some will be horrifiedSeeingCamelCase, others will_see_underscores_as_ugly. Some will fight for apps hungarian, some will lead a crusade against hungarian notation as a whole.

The opinions are varied, exactly because there is no hard data to support any single position. Your opinion is yours to have, and you may live in a time and place where people you interact with share the same opinion, giving you an illusion of importance. One step outside of your bubble will show you otherwise - it's just an opinion, one of many, and not magically better just because it's yours.


You can call me narrow-minded all you want, I'm not going to argue with someone who has this[0] as argument.

Otherwise I'll be happy to discuss about whether multiple assignments is readable or not, but when the majority of people think it's not, maybe the onus is on you to at least advance some arguments?

[0] https://news.ycombinator.com/item?id=26790701


> whether multiple assignments is readable or not,

That's the whole point: multiple assignments (and everything else) are NEITHER readable NOR unreadable, objectively.

There is no way to argue either way. Appeal to majority is a fallacy, so it's a non starter. Other than that, there's no data, no hard facts, no measures, nothing to talk about!

Again: multiple assignements (and everything else) may feel easier to read to you. It's just a feeling. There are bound to be people who feel differently. Both positions are equally subjective.

It's further complicated by the fact that readability is NOT a simple function of lexical structure. The exact same line of code can be unreadable to you at one point, then once you learn the intent or rule behind it, it magically becomes redable. It just "clicks" and suddenly you have no problems with reading it.

There are some properties which seem to correlate with readability, like predictability. There are some hard constraints due to how the eye is built. Even if you narrow those as much as possible, the space of possible syntaxes and styles which fit in these is absulutely HUGE.

I mentioned APL. This is an example taken from the front page of APL wiki:

        ','(≠⊆⊢)'comma,delimited,text'
    ┌─────┬─────────┬────┐
    │comma│delimited│text│
    └─────┴─────────┴────┘
And this is the commentary below it: "many functions can be expressed in fewer characters than even the shortest fitting name. For example ≠⊆⊢ is but three characters, while you would need five for the name Split"

They argue that "≠⊆⊢" is more readable than "Split". And, to them, it IS - once they learned the language. This suggests that readability is much more dependent on your knowledge, skills, and habits, than on the objective shape of piece of code.

I went out of my way to learn the widest possible variety of programming languages. The spectrum of things considered readable or not is so wide that it's really hard to find ANY points common to all of the languages. If there even are any!

So, again, step outside of your bubble and your mind will be blown.


Your "opinion" is particular "stupid" (your words) since you introduce a false analogy between program code structure and natural language sentences.

Let's see what CMU's SEI has to say: https://wiki.sei.cmu.edu/confluence/display/c/DCL04-C.+Do+no...


I know SEI well.

I have worked on projects where linters enforce that style, and I hate it.

You can read their justification: a programmer or code reviewer might mistakenly believe that the two variables src and c are declared as char *.

"A programmer might not know the syntax" is as stupid a justification for enforcing these rules as "a reader might not be able to read long sentences correctly" is for enforcing a stupid rule over text written in a natural language. I'm sorry that you failed to see the analogy.

They also just drop a statement like "declaring no more than one variable per declaration can make code easier to read and eliminate confusion." I can drop my opinion too: "declaring more than one variable per declaration can make code easier to read and eliminate confusion." It can also make the code shorter and easier to scan & edit.

And I think analogies involving natural language are more relevant than you think.

I would rather read "U, V, and W are phases" than "U is a phase. V is a phase. W is a phase." It's the same goddamn thing, we're declaring things (and optionally initializing them).


I'd say start learning in C++ and slowly transition to C. That's what I did; it was worth it. There are a lot of good resources in learning C & C++.


Any book or website recs for c++?


> (-11) % 3; // => -2, as one would expect

Nope, it's very unexpected. Because parity check can now return 3 outcomes.

(-3) % 2; // => -1


C is easy to learn, but very hard to master.

Having self learned C devs do a big, complex program is an invitation for security vulnerabilities.

Most of the time people find themselves fighting against the language, and struggling to have the program simply not to crash.

If you see that, you start to think how much those guys would have time left to ensure anything like input sanitation, or code hardening in general.


Nowadays it's more like:

>Most of the time people find themselves fighting against the language, and struggling to have the program simply to ...

compile.


For someone planning on learning C in the next couple of months (through K&R), do you have any advice on where to learn best modern practices afterwards?


I like Effective C: An Introduction to Professional C Programming https://nostarch.com/Effective_C but I would since I wrote it.


I like it too, very much so, to the point where I recommend it to people that find K. N. King's "C Programming: A Lengthy Approach" too intimidating. And I had nothing to do with writing it, as you obviously know.

My sincere thanks for the book.


Unfortunately not, and I see it as a big downside of C where you can only learn about important corner cases with your own bitter experience, direct instruction.

Advanced C usage remaining a black magic is not strength, it's weakness. This assures that attackers will always have a significantly stronger upper hand against industry's salaryman developers.


"Advanced C usage" can you explain this, please? What in C is so "advanced" that it's more black magic than C++ or Rust or any other high level language?

With C, what you see is what you get. I don't see how that can possibly be black magic.


The problem is that you don't see that much.

Compiler these days optimise the code beyond human comprehension. Memory management libs, and what hardware does with your physical memory layout is inscrutable.

Register level vulnerabilities are very much black magic for anybody, but silicon maker itself.

Memory management libs used by any big C program add to complexity.

Fancy runtime pointer, and ROP sanitation libs fix some types of vulnerabilities, and add another.

And so, and so, and so.

I am a big proponent of C, but will not hide that C needs substantial education beyond just programming to use properly, and safely.


The author recommends -std=c99 (-pedantic) default flags, yet suggests K&R (2nd edition, presumably) as a reference...


Also, why still use the old confusing long, short, long long etc... types when the much better fixed-width types had been standardized in stdint.h since C99 (even the Visual Studio C compiler has those since at least VS2015).


Little reason to not use at least -std=c11 these days


Linux is C99, most microcontrollers use C99 or even C89. It would be nice to get the new features, but it's not always possible.


> most microcontrollers use C99 or even C89

Most microcontrollers are ARM Cortex-M and are programmed with a recent GCC toolchain


MSVC has C11 support only since very, very recently:

https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-...

(it has a solid subset of C99 since around VS2015 though, just _Generic was missing - and ignoring VLAs, which have been "dropped" in later C standards anyway).


Verifiers haven't caught up to C11 yet.


Y=36000?


My takeaway from this thread is that C programmers are completely insufferable.


The one thing that is hard to tolerate in this thread is the negative comment that makes sweeping generalizations about other people based on their choice of programming language.


You haven't been in many programming language discussion threads have you? ;)

IME C coders are a fairly relaxed bunch on average, definitely more relaxed than some of the younger and more "radical" language communities in C's neighborhood.


Yep we are completely insufferable, now let us be :)


Why? I see one subthread with a holy war on code style, but nothing else.




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

Search: