
C for high level programmers (slides) - harel
http://charliethe.ninja/slideshow/introtoc.html
======
_kst_
Slide 31 says:

    
    
        Actually, this is how arrays are handled in C.
        A C array is a set of consecutive memory addresses.
        The first value is pointed to by a pointer.
    
        int a[] = {1, 2, 3}; // create an array
        // a is just a pointer to the first element...
    

No, "a" is an array, not a pointer. Defining an array object does not create a
pointer. The expression "a" is implicitly _converted_ to a pointer to the
array's first element in most but not all contexts.

If "a" were nothing more than a pointer to the first element of the array,
then "sizeof a" would yield the size of a pointer rather than the size of the
array object.

This is all explained very well in section 6 of the comp.lang.c FAQ,
[http://www.c-faq.com/](http://www.c-faq.com/).

~~~
sjolsen
It's also worth pointing out that there are no array parameters. If you write

    
    
        void foo (char bar [42]) {
            // ...
        }
    

then within the scope of foo, bar will be a _pointer_ to char, not an array of
char. You _can_ , on the other hand, write

    
    
        void foo (char (*bar) [42]) {
            // ...
        }
    

in which case bar will be a _pointer to array of 42 char_ (and notably, _not_
a pointer to a pointer to char!).

C does some very strange things. There usually is (or at least was) a good
reason for it to do so, but especially if you're a beginner, you need to be on
your toes if you really want to use it well.

------
TheAceOfHearts
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/)

~~~
thelogos
Personally, 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
My 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
Interestingly, 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
_I 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_
_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._

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.

------
StephenFalken

      C is quirky, flawed, and an enormous success. While accidents of history surely
      helped, it evidently satisfied a need for a system implementation language
      efficient enough to displace assembly language, yet sufficiently abstract and
      fluent to describe algorithms and interactions in a wide variety of environments.
    
      -- Dennis M. Ritchie (in "The Development of the C Language" [1])
    

[1] [http://heim.ifi.uio.no/inf2270/programmer/historien-
om-C.pdf](http://heim.ifi.uio.no/inf2270/programmer/historien-om-C.pdf)

------
okasaki
These slides were full of dangerous, elementary errors when they were
submitted to /r/c_programming a couple of days ago. The author doesn't know C
enough to have worthwhile views on the language.

------
billforsternz
I was quite impressed how many important concepts are covered in such a small
presentation. He explicitly doesn't cover the things in C that programmers
from other domains will understand easily (functions, conditionals, loops
etc.) and goes straight to the things that make C different.

------
htor
I feel C is best summarized in these two slides:

[http://charliethe.ninja/slideshow/introtoc.html#22](http://charliethe.ninja/slideshow/introtoc.html#22)
[http://charliethe.ninja/slideshow/introtoc.html#25](http://charliethe.ninja/slideshow/introtoc.html#25)

------
joshuapants
I just about died laughing at slide 22. Anybody know how these slides were
made?

~~~
charlesL
Hey author of the slides here, glad you liked it :D

I used [http://remarkjs.com/](http://remarkjs.com/) to make the slides. All
you have to do is include the script, add a textfield with your content in
markdown and it automatically converts to a slide show.

~~~
saadel
Can you tell us what font you used?

~~~
caipre
It's all there in the CSS. Looks like "Amaranth" and "Droid Serif".

------
pkulak
I was hoping this would actually tell me how to accomplish something in C. I
know all about pointers and memory, but I don't know anything about the
current state if C development. What libraries do people use? What are common
memory management strategies? Etc.

~~~
yoklov
Libraries depend on what you need to do. A lot of work is done with just the
std lib.

For domain specific stuff you use domain specific stuff. For generic stuff,
well, you tend not to want generic data structure libs that come with other
languages stdlibs because its hard to do those both efficiently and cleanly
(you can only pick one). Its easy enough to hack up a dynamic array or a hash
table with a fixed capacity that can only insert and get, maybe delete, so you
tend to do this when you need it. (Also, a sorted array usually does very well
in place of a hash table with not much code)

Memory managment it depend what your doing. I use a lot of arenas, and pools,
and as a result very rarely have to worry too much about memory management.
Some things become harder here like dynamically sized arrays, but you can do
this with chunks of fixed length arrays. (or whatever)

I have to do very little string processing most of the time (and when i do
they usually have small, known maximum lengths), and i imagine this memory
management technique would work less well for that.

------
akkartik
No mention of undefined behavior? That's the first thing I tell people about
C, considering how easy it is to trigger.

~~~
charlesL
The issue with undefined behavior is it's kind of hard to... well... define.
It's an odd combination of unsafe memory, float/integer rollover, and rules
with memory allocation. I wasn't quite sure how to clearly state it.

~~~
akkartik
Yeah I tend not to get hung up on the different ways it can trigger. It's more
like, if you trigger it your program might run fine for now and then suddenly
act up one day. It might die before the undefined behavior. It might launch
nukes. Still sure you want to get into this? No? Want to go back to Ruby?
Well, Ruby is built atop C. That's the five states of grief I try to get them
through.

~~~
kibibu
Worse than that - undefined behaviour can cause optimization to make your
software insecure.

[http://blog.llvm.org/2011/05/what-every-c-programmer-
should-...](http://blog.llvm.org/2011/05/what-every-c-programmer-should-
know_14.html)

------
vezzy-fnord
_command line utility development, writing a replacement to systemd_

Those don't require direct use of C, necessarily - but rather some form of FFI
to POSIX and the syscall interface. Hell, if one is writing a systemd
replacement, I'd encourage use of OCaml. It'd spare you lots of boilerplate
because you're writing relatively high level userspace logic, anyway.

~~~
charlesL
Huh, I've never actually heard of using OCaml for system programming.
Interesting!

~~~
danieldk
Red Hat's Richard Jones (who is also often commenting on Hacker News) does a
lot of system programming in OCaml (mostly VM management-related I think):

[https://rwmj.wordpress.com/tag/ocaml/](https://rwmj.wordpress.com/tag/ocaml/)

------
inglor
Why do presentations always get interpreted vs compiled wrong? It's not a
property of a language it's a property of the runtime. For example Java is
interpreted on old versions, JIT compiled on the desktop and compiled AoT in
Android...

~~~
nemo
Compiled vs. interpreted is intended there to mean something a little
different than runtime behaviors.

Compiled - source files are run through a compiler which produce some output
file that's then run. With C or Java you run the .c or .java file. C compiles
to an executable, Java compiles to byte code.

Interpreted - source is fed to an interpreter which typically turns it into
some kind of representation that's immediately run with no intermediary step
on the part of the user/programmer. Perl, PHP, Ruby, Python, JS, TCL, etc. are
typically run this way.

The terms are inherently a little muddy, since you can compile some
interpreted languages to bytecode (common in PHP, and how JRuby/Jython are
often run), and in theory could write a dynamic loader for C / Java to run
them as interpreted - no idea if someone has been so perverse as to do it.

The behavior of a JRE or other runtime in loading bytecode/whatever to execute
- JIT, AOT, or whatever has to do with how a runtime handles bytecode that's
already been compiled from source and turns it to machine code to execute. The
terms show up there also, but have a different meaning.

That's not what people are typically discussing when they talk about a
compiled vs. interpreted language, and the author was correctly using the
terms when referring to languages to draw the distinction in what a developer
does (rather than what a runtime does, which is basically irrelevant to C).

~~~
Sacho
I think the parent was correct.

In your explanation you even state "an interpreter which typically...". You
talk about compilers and interpreters, which aren't part of the language.

The way you describe it, compiled or interpreted is a transitive property of a
language, based on a popular way to utilize it.

If the definitions are "inherently a little muddy", then they're not very
useful. Javascript and compile-to-JS languages have a ton of compilers written
for them. Does that make them compiled or interpreted languages? How popular
does compiling a language have to be for it to become compiled instead of
interpreted?

It's no wonder that the parent prefers the strict definitions which lack this
ambiguity.

~~~
nemo
I think the slide was correct in expressing the idea it wanted to express
using terms that are commonly used in the sense they used, while the parent
was being pointlessly pedantic in wishing the terms had some strict sense that
was the only true definition.

Sometimes terms do have very strict definitions that are only correctly used
in a limited sense. In the case of compiled vs. interpreted that's not the way
things are and criticizing an introductory doc. for not adhering to an
arbitrarily selected strict definition is pedantic and counter-productive.

~~~
inglor
No, seriously. All the lecture had to say to be correct and at least as useful
is that C requires a build step unlike a lot of high level languages which
don't.

Nothing about compilers vs interperters, none of that is relevant and the fact
it's totally - incorrect is just icing.

This is not a terminology debating society, you're welcome to look up the
definitions on wikipedia or take an introductory CS course if you're not sure
what a compiler is or the difference between a language and a
compiler/interpreter for it.

~~~
nemo
You're right that if the slide said C requires a build step unlike a lot of
high level languages it would be correct. The terms "interpreted language" and
"compiled language" have a sense in normal usage that I described, and I just
verified that Wikipedia doesn't agree with you. There are no standards bodies
that define formal definitions for those terms, common usage is how they are
defined. All this is pointless pedantic quibbling, though, so it wasn't really
worth my time, nor is it worth yours, really.

------
readme
From slide 12:

    
    
        int my_var = 3; // It's an int!
        my_var = "abc"; // COMPILER ERROR! You clearly stated that it was an int!
    

"abc" gets interned and has a memory address. The statement my_var = "abc"
tries to assign that address to my_var, but since it's not a pointer to char
it gets cast to int instead, possibly truncating the value.

The program still compiles, just printing a warning.

------
allcentury
As someone who just reread "C Programming Language", in chapter 1 they teach
you how to count occurrences of characters so I'm not sure I understand the
sarcastic tone of "you're not going to count foo in a file". C can be used for
many things, I don't think the author of the slides dod a great job of
describing "when" C is the right tool for the job.

~~~
109876
I think he is just saying that C isn't worth learning if you just need to do
really simply things that a ton of modern languages can do in one of two
lines.

~~~
dmix
Or, you know, old-school 'programming' by combining Unix utilities from the
1980s.

    
    
        $ wc foo file.txt

------
rorykoehler
C was my first language and I find knowing it makes me a better programmer in
higher level languages. Apart from that though a great intro.

------
gshrikant
As someone who occasionally dabbles in C code, I am interested in knowing what
is the modern take on `goto`s.

I know they are "harmful" but I often come across code riddled with goto
statements [1] and I personally feel that as long as it makes the code
readable without significantly obfuscating the logic, goto is a perfectly fine
way of doing things (although popular opinion and consideration for best
practices have more or less forced me to remove goto from my list of C tools).
Also, the fact that it maps almost directly to asm makes it easier to reason
about the generated machine code (although if that's a significant reason for
using goto is questionable).

[1]
[https://github.com/zedshaw/mongrel2/blob/master/src/http11/h...](https://github.com/zedshaw/mongrel2/blob/master/src/http11/http11_parser.c;)

Zed Shaw's Mongrel2 server code linked to in another thread.

~~~
yoklov
Goto is fine. The 'harmful' style was using them in favor of ifs and loops.

The things I've seen people do to avoid a goto are pretty awful though. If you
ever use a 'do {} while (0)' just to break out of it, you should feel bad.
Goto is much clearer and cleaner than nonsense like that.

~~~
prav
Agree with above comment. Linux kernel code, especially device driver code use
goto a lot.

~~~
Gibbon1
I think one uses goto's for two sometimes three reasons.

1\. Sometimes one feels the need to abuse exceptions. But C doesn't have
exceptions. So one abuses the old goto instead.

2\. Sometimes the code is far more readable if you use a goto to short circuit
a complex block of code. Which will become insufferably more complicated if it
has to say keep track of a trivial case. if(this and not trivial case) else
this and not trivial. if(case a and not trivial case) else {if not trivial
case)

3\. You can use long jumps to do exception handling stuff. I've never had to
actually do this.

One comment. I remember trying to read ancient code that abused goto's mostly
because the programmer was desperately trying to fit everything into 4k of
prom in languages that didn't support structured code. That was the kind of
stuff Dijkstra was bitching about, not uses 1, 2, and 3. And actually since C
has always had modern control structures goto just is not abused much in
practice. Probably the opposite.

Side note.

int my_var = 3; my_var = "abc";

Just usually generates a warning when compiled. If run my_var will usually get
loaded with the address of "abc". If you follow it with the statement

printf("my_var=%s\n", my_var); // this will throw a warning

It'll print 'my_var=abc'

------
eru
C is a horrible language to write a compiler in. The other reasons for `Why
you should learn C' are better, though.

~~~
kgabis
Why? Compilation times are quite important, and writing a fast compiler is
easier to achieve in C than in most other languages.

~~~
eru
Premature optimization. Write correct first, profile then optimize later.

~~~
kgabis
Why is writing a compiler in C a premature optimization? Ignoring performance
is just bad approach to writing software. And "premature optimization" phrase
is overused, even when it's not relevant to the discussion.

------
bhaak
I misread the title as "C for high level programm _ing_ " and was a bit
confused. Of course, long time ago, C was considered a HLL.

The presentation is quite good. I might point people to it in the future so
they might at least have an idea in what dangerous place they want to venture.

------
jbenner-radham
So as someone who has an unhealthy love of C I have to say this is amazing and
will be forwarding it on to some friends trying to learn C. The humor is just
fantastic IMHO.

~~~
qohen
May I suggest that you also send them a link to Zed Shaw's "Learn C The Hard
Way" online book [0], which presents lessons in modern C programming,
including the use of tools like Valgrind, etc.?

[0]
[http://c.learncodethehardway.org/book/](http://c.learncodethehardway.org/book/)

~~~
TheAceOfHearts
I just wrote a comment [0] about my problems with learning C, would you say
this book covers the issues that I raised and is worth reading?

EDIT: Well, I just looked over the chapters in this book and I'm now extremely
excited to give it a read. It seems to cover most of the topics that I'm
interested in, so thank you SO MUCH for sharing!

[0]
[https://news.ycombinator.com/item?id=9636122](https://news.ycombinator.com/item?id=9636122)

~~~
14113
I think it might not cover them, going by this: [http://hentenaar.com/dont-
learn-c-the-wrong-way](http://hentenaar.com/dont-learn-c-the-wrong-way)

------
chrixian
The thing about pointers that I don't get is why do you need the memory
address of the variable? Is that the only way to get the value when you want
it? Like, every variable has to have a pointer in order to make use of the
variable?

~~~
andrepd
If you want arrays of variables, you need pointers. It's not enough to know
the value of an int (the first element of the array), but you need the pointer
to an int (pointer to the first element of the array). Now you can increment
the pointer to go to the next element. You couldn't do this with a simple
value.

Also, suppose you want to pass a variable of a large data type, like an image,
to a function. Instead of copying the entire variable, just pass the cheap
pointer. The analogy is giving someone an URL vs the source code of the site
for them to paste in the browser (You can't fit the latter onto a QR code, for
instance, but you can the former).

~~~
kyllo
Of course the problem is, if you're passing a pointer to a data structure to a
function, the function doesn't know the size of the data structure unless you
pass that as another argument.

~~~
xamuel
You meant to say, "if you're passing a pointer to an array to a function, the
function doesn't know the size of the array unless you pass that as another
argument".

When passing a (pointer to a) data structure to a function, in 99.99% of cases
there's only one data structure you'd pass, and you build this into the
function's prototype, e.g.,

    
    
      int myfunction( struct my_structure *x )
    

instead of

    
    
      int myfunction( void *x )
    

and so, yes, the function does know the size of the structure. And in the case
of arrays, often it's enough to mark the end of the array (with '\0' in the
case of char arrays or NULL in the case of pointer arrays), I'd only roll my
sleeves up and worry about minimizing length calculations if I had actually
done some profiling and determined that such nitty-gritty optimization was
needed (it rarely is).

------
cosmolev
C is syntactic sugar over ASM

~~~
iopq
Really? I forgot the sugar for registers in C, can you remind me?

~~~
benwaffle
the register keyword

~~~
ufo
Which most modern compilers just ignore.

------
smilefreak
These slides are great, I was thinking of presenting something similar to a
bioinformatics lab group I am a part of. Could I adapt some of these slides?

------
velox_io
This is why I like C# so much, you can go from LINQ & generics to pointers. So
much versatility.

~~~
72deluxe
I remember someone using LINQ to do fancy things on the results of an SQL
query. It was stupid. Far better would be to have written the SQL properly in
the first place, instead of grabbing loads of data and doing cartwheels
client-side.

LINQ is neat, of course.

------
halayli
teaching C and not checking malloc, bad idea. using realloc in the way its
used in this "tutorial" can cause memory leaks. realloc should be checked
before reassigned because it can return NULL and that will overwrite the
previous valid memory address.

~~~
mappu
_> teaching C and not checking malloc, bad idea_

On Linux, malloc will appear to give you memory even if there is none left to
give, so checking the error is not important.

~~~
pdw
Not checking malloc's return value can easily lead to security
vulnerabilities, particularly in bytecode interpreters and things like that.

The basic plan is simple: Trick the target program into allocating an
impossible huge block of memory (e.g. 3 gigabyte on a 32 bit system). Malloc
will return NULL but the program blindly assumes the allocation has succeeded.
Now use carefully chosen indices to read and write whatever memory you want.

As an example, here's a classic Flash exploit that used this technique:
[http://chargen.matasano.com/chargen/2007/7/27/this-new-
vulne...](http://chargen.matasano.com/chargen/2007/7/27/this-new-
vulnerability-dowds-inhuman-flash-exploit.html)

Really, xmalloc is five lines of code. Just use it.

    
    
        void *xmalloc(size_t size) {
          void *ptr = malloc(size);
          if (!ptr) abort();
          return ptr;
        }

