257 comments

[ 3.9 ms ] story [ 258 ms ] thread
I guess no pre-ordering, the link is broken: https://www.manning.com/books/modern-c
Wow, I wish more technical books used LaTeX for typesetting like this. Beautiful.
Since you mention typesetting: I am working on a volunteer project that is transcribing old, out of print books from the 1700s through early 1900s (all public domain). For now the plan is simply to make them available online formatted for web, e-readers, and PDF, but thought is being given to formatting for print-on-demand. We've never done the latter so we have no idea what tools would be best for that: is there a good reference for free tools/software for doing page layout for physical books?
LaTeX or TeX would be excellent choices if you want good typographic control, though there's a significant learning curve if you're going to produce beautiful books. Check out https://www.ctan.org to start learning more.

They have lots of book publishing resources: https://www.ctan.org/topic/book-pub

Context would be a convenient tool for the job. You can create pdf, xml, and epub from the same source. You get the power of Tex but producing modern looking stuff is IMO much easier than with Latex. I personally usually use Latex for short articles and Context for books.
> Beautiful.

Is it? I skimmed over it, and I don't find the typesetting / listings / tables / highlighting particularly appealing.

I wonder if it used a publicly available style? I like how some stuff looks, I'd like to experiment with this.
Honestly, I prefer Manning's regular typesetting, which is also beautiful and clear.
I hate LaTeX typesetting, it looks very archaic and reeks of academism (is that a word?). Unless document require lot of formulas, LaTeX is not a right tool in 2019.
You probably don't hate Latex typesetting. It's just that the kind of default styles that people, especially beginners, tend to use that often look archaic. Latex doesn't make creating your own unique styles too easy though, which is why I personally prefer Context.
Insane take. Latex can look like anything you want and also has amazing microtype features these days
Of course it can, but by default it does not look good, and it is not trivial to change things, hence you see documents which look straight out of 90s.
Is this file not opening for anyone else? Can't seem to get it to open?
Some PDF viewers have trouble with it.
Is this a good book, can anyone comment?
I'm not a fan so far. Lots of deliberately bad C code. Using uninitialized variables, etc.

Explanation of jargon seems to take priority over explanation of the language - the term "string literal" is explained before the concept of a function.

What book would you recommend as an alternative?
C Programming: A Modern Approach, 2nd Edition by King is a good beginner book.

21st Century C by Ben Kelemens is also a good book.

> C Programming: A Modern Approach, 2nd Edition by King is a good beginner book.

I would recommend that as well. Dr. King was one of my favorite professors, but I bought that particular book on my own.

I would second 21st Century C, it's an excellent book. It has a very pragmatic approach, skipping features like unions that are not used in modern C, but going into great detail on the important things like memory management.
Thirded, especially on the pragmatism, this by far the most pragmatic programming book I've ever read. I fell in love with the C as a scripting language parts (https://github.com/RhysU/c99sh), now I don't have much of a use for a scripting language to sit between bash and "proper language" (apart from some occasional awk).
Glad it has been of use!
Huh? Since when are unions not used in modern C? Tagged unions are a really common pattern in language interpreters. What are they supposed to be replaced with?
How about Zed Shaw's Learn C the Hard Way?
I just finished Learn C the Hard Way and would be interested in a comparison between it and those other books mentioned.

Just from a quick glance, Modern C appears to be a much more in depth book. It appears to cover things like memory alignment, advanced types like unions, malloc, threads, etc.

Learn C the Hard Way is more of a quick introduction. You learn the basics, like pointers, basic data structures, basic types and then learn to do something interesting with those basics (build a web server). It also teaches some valuable real life practices that Modern C doesn't touch on, like how to structure your project, makefiles, how to use Valgrind and gdb.

Wait what, Learn C the Hard Way doesn't teach malloc? Programming without dynamic memory allocation is pretty restrictive. What "data structures" does it even teach if it doesn't cover malloc?
Like I said, it teaches the basics. There's a single page on heap vs stack allocation and it says malloc allocates memory to the heap. That's it. That's all you really need to know to do stuff and it doesn't go into what malloc actually does under the hood.

Modern C has an entire chapter called "malloc and friends" where it goes into much more depth about malloc, calloc, realloc, etc.

The book is a good introduction to programming in C and I'm glad I read it. You obviously have a history with the author and I'll thank you to keep your past impressions out of this particular discussion.

> There's a single page on heap vs stack allocation and it says malloc allocates memory to the heap. That's it. That's all you really need to know to do stuff

Every other C book seems to disagree, but OK.

Does the book cover the -> operator? Does it mention free and realloc? You need those to do stuff, at least.

> You obviously have a history with the author

How is this obvious, since it is incorrect?

I'm not interested in continuing this line of conversation. Read the book if you want to know more.
I've only skimmed the book, but I wouldn't recommend it. The author has a tendency to be snarky and come up with "fixes" for "mistakes" that "old" C programmers make, except he does so in a way that is at odds with the fundamental design of C constructs and does not really show a correct understanding of undefined behavior.
I vaguely recall the whole fallout where it was revealed he clearly did not understand undefined behavior, and I believe he did walk back and accept his misunderstanding.

That said, yes it is damned annoying to have someone that doesn't understand the intricate details of the language poop all over that community and act as if "well i've contributed lots of code in C, why listen to a bunch of language lawyers, that isn't important anyway" as if the two are mutually exclusive. There are some of us that have been using C for years for actual work and are also intricately familiar with the prickly details of the standard. Those are the people that you should be looking for C books. Zed Shaw consistently shows he would rather hear the sound of his own voice and diminish things that are uninteresting to him rather than put in that work to become an expert on all facets of the topic he is claiming expertise.

https://what.thedailywtf.com/topic/16422/zed-shaw-gets-schoo...

An Introduction to C & GUI Programming - Simon Lon is also good for beginners, especially it's easy understand content and beautiful typographic style.
The C language can be best learned from "The C Programming Language, 2nd Ed" [1], also known as "K&R book". Pair it with excellent notes by Steve Summit [2], and you don't need any other book to master C.

[1] https://www.amazon.com/Programming-Language-2nd-Brian-Kernig...

[2] https://www.eskimo.com/~scs/cclass/notes/top.html

No doubt K&R is second to none, but it only covers C89. Since then we have 99, 11 and apparently now 17 too?
A useful approach is to learn 95% of the language from K&R, and then refer to the newer standards for the incremental changes.

If there is a book as good as K&R and covers the new ideas, I'm interested to learn about it! :-)

My two favorite C books are:

Head First C by Griffiths and Griffiths

and

Expert C Programming: Deep C Secrets by van der Linden

Love Deep C Secrets. I have it and flick through it whenever I want to remind myself about the dark corners I never mastered during the last 20 years of programming in C.
> Lots of deliberately bad C code. Using uninitialized variables, etc.

You do realize he's doing that for illustrative purposes, right?

Yes. As I said - it was deliberate. That does not excuse it.

By the very fact that this is a book, it presumes that the intended audience consists of visual learners. When working with visual learners, if something is erroneous, it is imperative that it be explicitly and visually called out as such. In linguistics pedagogy, for instance, this is done by marking any ungrammatical construct or example not found in natural language with an asterisk every time.

https://www.ling.upenn.edu/~kroch/courses/lx101/assign96.htm... https://linguistics.stackexchange.com/questions/338/meaning-... https://www.departments.bucknell.edu/linguistics/lectures/as...

This book does not do this. It freely uses deliberately bad code in example programs. This can only result in confusion on the part of learners. It is therefore inconsistent with pedagogical best practices. I would not recommend it.

> Takeaway 1.5.5.1 All variables should be initialized.

pg 59

[edit]

This was originally a comment about a book I thought was the same but merely had a similar title.

Looks reasonable to me. I've been programming C daily for 25 years. Cosmetic but it is nice to see "int const foo;" being preferred to "const int foo". And "We define variables as close to their first use as possible". Less cosmetically, section 5.6 on named constants is great. That's a fiddly area of C from which to pull out reliable advice.
There is no technical reason to prefer one or the other; they are both declaration specifiers which can occur in any order. long const int is valid, even.

The const qualification is in the type; it is not a direct attribute of foo, so putting the const closer to foo means nothing.

The point isn't about putting it closer to the variable name, but consistently to the right of what it applies to.

But yes, it is a matter of opinion.

I'm only an infrequent C programmer, but that goal interests me simply because it seems unattainable given other aspects of C's syntax. If qualifiers went to the right of things they qualify, I'd expect variable declarations to look more like:

  foo: int const;
And similarly, function declarations might also take a more ML-like syntax. C seems more like it wants to be an adjectives-before-nouns type of language.
See wahern and my response to the grandparent.
So, IOW, it's not purely a point of style, it's more about what's a sane way to write things given the requirements of the language's syntax when you're dealing with pointer declarations?

Which would explain why it seems so odd to me; I spent a fair amount of time in C-style languages, but typically only ones that lack pointers.

I think it can be both, plus communication: communication to n00bs, to yourself in the future if you're really tired, etc.
Though, if the goal is communication, wouldn't it be clearer still to use typedefs to clean up more complex declarations a bit? Or does that end up making things worse?
People do that if such a type is used a lot but probably not if it's just a one-off, just like when they write a function instead of using the same snippet a lot but don't if it isn't.
To the right of what it applies to? Like French: Moulin Rouge. :)
In French it can be either left or right depending on the word: un moulin rouge (a red mill) but un vieux moulin (an old mill)
It's a good habit to get into, though, when it comes to pointer types. How many times have people written code like:

  const int *foo;
when they meant

  int *const foo;
or

  const int *const foo;
Rarely does someone mean:

    int *const foo;
It's mostly useless to declare a variable that cannot be assigned. But very often people write

    const int **foo;
When they intended

    const int * const *foo;
> It's mostly useless to declare a variable that cannot be assigned.

So all functional languages (where immutability is the default) are mostly useless?

If you declare a variable in this way in C, it can never be assigned. Not once — zero times.

What is or is not good practice in C has little bearing on or relationship to functional languages, so I'm not sure why you bring them up in this context.

FWIW, I find that form quite useful for defining short aliases, e.g.

    int *const current_foo = &items[i].bar.foo;
before some complicated manipulations on current_foo.

(A decent (SSA-)optimizing compiler will produce identical code with and without the declaration.)

Yeah, this only works if you initialize at the same time as declaration. And as you say, without const, the same code is generated. All it prevents is accidental reuse of current_foo, which may have some value.
const-qualified pointers are largely useless. If a struct contains such a thing, it can't be dynamically allocated. As a function parameter, it doesn't protect the caller from anything because passing is by value.

Let's see how many times this occurs in my TXR project (~75K lines):

  $ git grep -F '*const'
  $ git grep -F '* const'
  linenoise/linenoise.c:    const wchar_t * const *lelem = convert(const wchar_t * const *, larg);
  linenoise/linenoise.c:    const wchar_t * const *relem = convert(const wchar_t * const *, rarg);
One place: a comparison function for a qsort call, where a vector of const wchar_t * strings is being sorted. I didn't write this (though I did convert it to wchar). qsort passes const void (star) pointers to the elements, which are const wchar_t (star), thus things end up like this.

How about Linux kernel 4.9? There are numerous occurrences of this involving arrays declared at file scope, like this:

  static const char *const foo[] = { "abc", "def" };
Let's filter those out to find other uses:

  # lines that start with at least one tab and contain *const,
  # possibly with optional spaces between * and const:

  kernel-4.9$ git grep '^[\t].*\*[ ]*const\>'
  kernel-4.9$
Wow, not a single result!

Let's relax that and allow a space or tab [\t ]. Then there are lots of false positives due to items in block commented lines starting with a ' asterisk' sequence. If we require at least one lower-case alpha character before star-const, we filter most of these out:

  kernel-4.9$ git grep '^[\t ].*[a-z].*\*[ ]*const\>' 
Documentation only, showing useless const parameters in a function prototype decl:

  Documentation/cpuidle/driver.txt:                            const struct cpumask *const coupled_cpus);
  Documentation/media/kapi/v4l2-controls.rst:                       s32 skip_mask, s32 def, const char * const *qmenu);
False positive, in a table inside a block comment:

  arch/powerpc/xmon/ansidecl.h:   PTRCONST        `void *const'           `char *'
Useless const parameter in function pointer signature. The definition of these operations are not required to use const:

  drivers/net/wan/lmc/lmc_var.h:        void    (* set_circuit_type)(lmc_softc_t * const, int);
  drivers/net/wan/lmc/lmc_var.h:        void      (* watchdog)(lmc_softc_t * const);
Real result:

  drivers/of/fdt.c:                 const char *const *compat)
Real result:

  drivers/staging/vc04_services/interface/vchi/vchi.h:   const char * const vll_filename; /* VLL to load to start this service. This is an empty string if VLL is "static" */
Fluff sibling:

  drivers/staging/vc04_services/interface/vchi/vchi.h:                                       void * const bulk_handle );
Low-value const parameter in function definition:

  drivers/usb/mon/mon_text.c:    char __user * const buf, const size_t nbytes)
Real result (Yacc/Bison skeleton origin):

  scripts/genksyms/parse.tab.c_shipped:    YYSTYPE const * const yyvaluep;
  scripts/genksyms/parse.tab.c_shipped:    YYSTYPE const * const yyvaluep;
  scripts/kconfig/zconf.tab.c_shipped:    YYSTYPE const * const yyvaluep;
  scripts/kconfig/zconf.tab.c_shipped:    YYSTYPE const * const yyvaluep;
Useless const pointer arg in prototype:

  tools/lib/subcmd/parse-options.h:                         const char * const usagestr[], int flags);
That's it; a mere spec of dust relative to the LOC coun...
(comment deleted)
Your grep result of Linux is because the regex is bad. `\t` does not seem to be available in the grep basic syntax. It is available with Perl Compatible Regular Expressions, but then you can't use `\>`.

Changing it to

  $ git grep -P '^[\t ].*[a-z].*\*[ ]*const\W' v4.9
returns 1,779 matches. Here are some from kernel/

  v4.9:kernel/time/posix-cpu-timers.c:    struct signal_struct *const sig = tsk->signal;
  v4.9:kernel/tracepoint.c:               struct tracepoint * const *end)
  v4.9:kernel/tracepoint.c:       struct tracepoint * const *iter;
  v4.9:kernel/tracepoint.c:               struct tracepoint * const *end,
  v4.9:kernel/tracepoint.c:       struct tracepoint * const *iter;
Many of the matches are the same as your "array of string constants" example, but just defined at local scope. And many are just constant local variables which some may not consider worth making const. The ones from tracepoint.c above are probably walking an array of const pointers by pointer instead of by index for example though, which is a common case where pointers to const pointers are used, which are legitimately useful.
When I see "const int *foo" that's when I open my browser and google "C const pointer syntax".

The fiddlyness of const type declarations is definitely one of my least favorite parts of const semantics in C and C++.

I think you just need to read right to left?

"const int * foo" means that foo is a pointer to an int that is constant.

Which could also be "int const * foo" foo is a pointer to a constant int.

But since the const qualifier for pointers can't be reordered like this, I think the point is that it's a better practice to have the const come after, so that it ALWAYS come after in your codebase regardless of the context?

Thanks for the explanation. I meant it mostly facetiously, but my more serious point is that there are some parts of some languages (and APIs and tools and architectures) where the answer you get from experienced practitioners is "think harder". It's not that I can't grok it, it's that it takes more effort than it should given how much the concept comes up, and the more I wrestle with my tools, the less I potentially get done.

C++ rvalue references are a great example. Whenever I see them I have to go back and relearn the concept because accounting for the language feature sometimes feels more complicated to get right than the unsafe pointer chucking it replaced.

I think it was needed for operator overloading.
The parent says it is "cosmetic", not technical. The parent, being someone who wrote C daily for 25 years, knows what the syntax allows and that it isn't necessary. A constant pointer to a constant pointer to a constant integer should be written "int const * const * const x" because there's only one other way, "const int * const * const x," which is inconsistent, ugly and potentially confusing.
It's bad cosmetic. The predominant style is "const int", following the English adjective before noun.
(comment deleted)
It depends on the perspective. To me and in the codebases I've worked in, the predominant style is "int const" instead of the other way around.
My perspective is that "const type" order consistently appears in all versions of the ISO C standard and POSIX.

Every major system API I can remember working with that didn't hide const behind a typedef (like Win32's LPCSTR and whatnot) has it const first.

Speaking of Microsoft, though the API's hide const with a typedef, the MSDN samples are predominantly "const char" order.

I've never seen a man page with "int const".

Searching all of the man pages I have installed on an Ubuntu 18 system here (literally grepping through all of /usr/share/man), I found just one with "char const" or "int const". Namely this one:

  $ man guestfs-hacking
This contains:

        int
        commandvf (char **stdoutput, char **stderror, unsigned flags,
                   char const *const *argv)
In the same man page, there are other declarations that have the predominant convention:

        int
        commandrf (char **stdoutput, char **stderror, unsigned flags,
                   const char *name, ...)
You just worked on some unusual code bases, and are glossing over the language and system API's those code bases depended on, and their respective docs.
Your response is an instance of the naturalistic fallacy.
Sure, except for the part where C is a language that uses English or English-based keywords, designed by native English speakers.
There's no "except" because one cannot conclude how people should write programs from how they do write programs, regardless of circumstances. In any case, languages and their use change. C is 47 years old. Consistent expression structures is more important than analogies to English.

Further, C is not like English and even if it were, one could not conclude that it should be or should continue to be. Moreover, that it was designed by English speakers is not evidence that it is like English or should be like English: most assembly languages were also desiged by English speakers.

It's not about moral goodness, it's about developer ergonomics. I don't see why the fallacy would apply to all value judgments
It's isomorphic, but you're right, I used an overly specific term. In any case, one can't conclude that something should be the way it is from the fact that it is the way it is without fruther premises, regardless of the kind of value judgements involved.
Implicit key premise seems to be that the more akin a formal (programming) language to it's users' natural one, the more ergonomic it is. I would not necessarily agree, but not read it as ought-from-is.
C declarators cannot in general and do not follow English grammar rules, so that is a non-sequitur.
const int isn't a declarator; it's a list of declaration specifiers.

Though these can be reordered, it's clear they were intended to follow English.

In a compiler implementation, it's troublesome to enforce the order compared to processing a simple list and setting/checking flags in some data structure, diagnosing invalid duplicates or mutually exclusive situations.

I think you're inadvertently just proving the point.

I was speaking of declarators, not declaration specifiers. In C, a pointer type is part of the declarator and not the specifiers. Therefore, the CV qualifier is also part of the declarator except for the one exception of the specifier. Regardless of whether specifiers were intended to follow English (and this claim is dubious), declarators certainly were not, since it's often more natural to read them right to left, and it's generally most natural to read the left most type-specifier last.

example: char *x[10]; Translated to english this is naturally stated as an array of 10 pointers to char. Not char something something array.

Edit: later on I see you've moved the goal posts about the "uselessness" of cv qualifiers on pointers. I don't know what to tell you, I think that's bullshit, and there are a number of viable use-cases for them regardless of whether you have encountered them. I'm just addressing the consistency part here.

Indeed, declarators must have their postfix chain read first, left to right, then the prefix portions right to left. (Separately at each level of parenthesization, if any, working inside out.)

There is no choice about where to put const inside a declarator; the position affects the nesting level which alters the semantics. So there is no debate to be had there.

Note that in declarators, the const is necessarily to the left of the thing it qualifies, so why would we put it to the right of int:

   int ** const ptr;
                ^^^ this is what is const

   int * const *ptr;
               ^^^^ this is what is const

   const int **ptr;
         ^^^ ^^^^^ these two are const
In the canonical specifier order, nothing to the left of const is tainted by const:

   extern const int **ptr;
                ^^^ ^^^^^^  const and const
   ^^^^^^ unrelated to const
For the best possible consistency, imagine const to be pissing, while the wind is blowing left to right.
Wait what??

Your third example could just as easily and correctly be:

  int const **ptr;
            ^^^^^ this is what is const
True, it is a special case when there is more than one item in the declarator list as to how const applied, but that by itself is secondary to where const goes.

Const qualifies the object. Whether the const is applying to the "int" in some way or is an independent type specifier that applies to each declarator is pretty philosophical. I say it's the latter. The fact is, the very allowance of a cv qualifier in the specifier list introduces an inconsistency or imbalance.

Ultimately this is one of the ultimate bikeshedding argument. There's no right answer, and appeals to spoken language aren't particularly compelling when bikeshedding C of all languages. This isn't AppleScript.

I'm not even terribly interested in this particular point. I'm more interested in quashing the notions that bikeshedded opinions of language syntax have some divine providence. It's just bullshit.

We're then missing that the const qualification is applying to the int type.

Also, declarations can have multiple declarators.

   int const *p, volatile **q[3];  // syntax error! not how it works
This isn't bike-shedding. The shed has already been painted. There is a right answer which is not to make your code look weird just because the standard allows it.

No "int const", no i[array] instead of array[i], no deceptive trompe d'oeil nonsense like:

   int* p, q;
You will not solve any issue in C programming by swapping around declarators.

When we write code, stuff that looks weird should signal a bug. When bug-free code looks weird, that is a distracting false positive that grabs my attention for no reason. Don't add gratuitous weird.

They literally cannot follow English, so I guess they weren't intended to. The order isn't arbitrary for all elements of the list, only the left-most.
I generally think of "int" as also an adjective, with both "const" and "int" describing the variable.
That's fair, and double is obviously an adjective. struct foo maybe not so much.
"I generally think of "int" as also an adjective, with both "const" and "int" describing the variable."

Eh? int is a noun, surely? How does int describe an int?

I can imagine cost and long being adjectives for the noun int.

(comment deleted)
The down-votes on "Hacker" "News" are amusing again.

Never argue in a technically correct manner here. Use misdirection, grandeur and talk in a presidential voice.

This is not comp.lang.c.

How is the DS 9000 for C11 coming along? ;)

Still only signed int for typed constants. Are there some plans to mitigate it? It's a pain point for FFI.
(comment deleted)
I really like the first edition. Old C diehards hate it because it breaks a lot of tradition. Personally I think it's really refreshing and made me appreciate C and lot more.
The first edition has been my go-to for introducing some of the newer (1999 and later) features of C (and some of the subtle footguns) to people. I'd definitely recommend it as a first introduction to C.
Contradiction in terms.
There’s a lot of new things in C since the K&R book came out.
Half of them cribbed from C++.
Not really; C has a number of things that C++ never adopted such as restrict and partial structure initialization (although this might be coming to C++).
Half does mean some are, some aren't.

"//" comments, function prototypes, const, atomic, inline, complex, mid-block definitions are.

Runtime variable local arrays, designated initializers, restrict not. C++ cribbed designated initializers from C, coming in '20. Restrict was cribbed, late, from Fortran.

> C++ cribbed designated initializers from C

AFAIU C++ will require that you initialize the members in the same order as defined. It also won't permit you to exclude any definitions. Why even bother?

Somewhat controversially, C permits you to define members multiple times, with the last definition taking precedence. Compilers sometimes warn about this, but I've personally found the behavior useful--I'll write an API that provides a macro with default values yet which allows the user to override any particular definition. I don't think I've ever had a bug in an initialization, at least not involving multiple definitions. That seems like a very easy and superficially useful diagnostic to write, but which prevents useful behaviors; behaviors that named initializer's were deliberately designed to provide.

In a C++ designated initialization, members may be omitted. (You were told wrong.)

There is probably no reason to enforce order unless unmentioned elements have a non-trivial destructor, so that could be relaxed in a future Standard. Members that need destruction would need to be destroyed in the opposite order; enforcing order allows reusing code already generated for the containing-object destructor, but in many interesting cases (e.g. C structs) there are no destructors to run anyway. In the others, there is no reason why it would need to re-use the class destructor.

C itself is an old language that lacks a lot of features that are near-universal in newer languages, but the language is still evolving, and there is still a valuable distinction to be made between how people preferred to write C decades ago and what's considered good style today.
On a related note, do we have any fully complaint C11 compiler for MS Windows or is using gcc under WSL (or a VM) the best option? I take it there is no C17 compliant compiler yet?
Clang and GCC both support C17 and there are binary versions for Windows see MSYS2. I think Pelles C also supports C11.
Does glibc support cthreads yet?
AFAIK, C11 threads are supported only in Linux. From a glibc point of view C11 threads are supported since v2.28.
C11 threads are also available on freebsd and possibly (don't know) other BSDs. Pelles c also supports them.
You don’t need WSL to run gcc on windows. You can use the mingw distribution to get a GNU tool chain that compiles a standard windows EXE.
Stuff people 'just know'

mingw: For native build and execution. A lot of Windows IDE's will just work with mingw.

msys2: For posix compatible build but native win32 targets. Useful when your project uses autotools. Like when trying to build someones library. msys2 just tends to work. Trying to get autotools to work under windows tends to exceed my patience, time and brain cells.

cigwin: for a posix compatible build and runtime environment.

Also gcc can be built as a cross compiler. The two times I tried it is was more straight forward that I would have thought.

cygwin

you can build also with gcc for cygwin, but you introduce a cygwin.dll (not sure about the name) dependency.

Note that msys2 isn't just for building programs, it provides a full bash environment with many unix tools and a package manager (pacman, same as in Arch Linux) to install more. The build environment is unixy enough to be able to build more tools from source using the classic ./configure; make; make install route (more or less, may need some modifications in the code).

For me my main use of msys2 is as a shell where i run scripts and command-line utilities with the build stuff being a distant secondary use.

Isn't modern C pretty much Rust?
No, for the foreseeable future (20-30 years) C will still be 10x larger than Rust in code and deployments...
Call me when Rust becomes the interface to the most used kernel in servers and supercomputers.
I wouldn't call syscalls anymore "C" than "Rust". Neither language can make them without library support or inline assembly. Both can with.
I think the parent comment was less about the capability of Rust, but more about the overwhelming momentum that C has.
(comment deleted)
And on that I'd fully agree with him.

My comment also isn't really a comment about rust. It's about a common misconception that the unix/linux kernel somehow talks in "C".

Libc talks in C, most of the other libraries talking for interfacing with the kernel talk in C. The kernel speaks it's own language that has no special relation to C at all.

Except that you'll be hard pressed to find a syscall without an accompanying C library. It's common for other languages to build upon that rather than call the syscalls directly. Specially during the early days of a new kernel feature.
A kernel's interface isn't necessarily syscalls. On macOS libSystem's C API is the API of the kernel, because syscall stability is not guaranteed.

On the other hand, pretty much every kernel has a C API.

In terms of overall language design and semantics, Rust owes a lot more to ML. I'm not sure there's really much of anything that it owes to C.

Agreed with the commenter elsewhere in this thread that Zig seems like a more reasonable contender for a modernized descendant of C.

(comment deleted)
I'd argue that Rust is much closer to C++ than to C or ML.

Rust was designed by C++ programmers, to replace code that was previously written in C++. The concept that most guides its design is the idea of "zero cost abstractions", which a very C++ thing as well. ML doesn't mind if abstractions have a cost, while C is a smaller and more spartan language.

>I'm not sure there's really much of anything that it owes to C.

Rust's default behavior of ignoring integer overflows when not in debug mode -to put an example-, put it very close to C.

The difference is that it’s well defined as two’s compliment overflow instead of UB. That’s a big difference.
Could we not do this, please?
Why?
This phenomenon is the Rust Evangelism Strike Force (same as/similar to rust jerk?).

I like rust. I think it may be able to obsolete many/most uses of C some day. But I will concede that folks go overboard w this sometimes.

This article has nothing to do with Rust. Bringing Rust up like this is inappropriate, and looks bad on the Rust community. It fuels an "us vs them" mentality, and doesn't win over any hearts nor minds. It often hardens them, instead.
You are such a tool
(comment deleted)
Do people still use K&R these days?
That's what I used first. I did a lot more study, but I think that's actually the only book I used. What did it for me was trial-and-error and practice, but the most important part was getting the basics down so I could start reading through other people's good code.
I can't speak for everyone, but I read K&R 4 years ago to supplement some of my classes in universtiy
If someone tells you to use K&R take that as license to ignore their advice from that point on.
I am using it to study C. Can you tell me the reason why you wouldn't recommend it?
I really enjoyed it, but it’s best supplemented with exposure to modern C syntax and an introduction to undefined behavior.
Despite being amazingly well written, the most recent edition of K&R was published in 1988. The current version of C is C18, released last year, so K&R is missing about 30 years of evolution. It's not a bad book, by any means, but it doesn't include important aspects about how C is used today.
Because it's 30 years out of date. It's missing three revisions of the language. Teaches a lot of practices that lead to obtuse brittle code riddled with security holes.

Avoid.

can you be more specific about these practices?
What ufo said

> implements strcpy as `while(dst++ = dst++);

The book is full of stuff like that.

Doesn't strcpy always have that issue? The terseness is one issue, but the security problem is that it implements strcpy at all without warning.
You tend to get warnings to use the _s versions of some functions (strcpy_s in this case) these days.
I just tried (with gcc 9.2.1), and got no warning. As far as I know, these _s versions aren't that good, and often aren't available at all; see http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm which says among other things

"[...] none of the popular Open Source distribution such as BSD or Linux has chosen to make either available to their users. At least one (GNU C Library) has repeatedly rejected proposals for inclusion [...]"

"[...] As a result of the numerous deviations from the specification the Microsoft implementation cannot be considered conforming or portable."

"often aren't available at all"

It's in C11. Perhaps Red Hat use non-compliant compilers, or very, very old ones?

It's an optional part of C11, which means that even a fully compliant compiler does not have to implement them.
strcpy_s has no manual entry on Ubuntu, while "man strcpy" and "man strncpy" work as expected.

Why would that be?

K&R is a well written book, and is very pleasant to read. IMO, it is one of those books that everyone should read one day or the other.

The main thing to watch out about it these days is that K&R is a bit carefree about the numerous ways where you can shoot yourself in the foot using C. For example, K&R famously implements strcpy as `while(dst++ = dst++);`. These days, most people would say that code is excessively terse, and that you shouldn't be using strcpy in the first place (because it can potentially overflow the dst buffer).

You meant:

while ((dst++) = (src++))

I believe.

while ((dst++) = (src++));
Both of these replies are missing the asterisks to indicate pointer indirection. Probably HN is eating each asterisk, treating it as emphasis or something.

Also terse it may be, but this (assuming the missing asterisks are restored) is a good, idiomatic implementation of strcpy. Whether strcpy is good or evil is a separate discussion.

Yes, but that doesn't make it a good idea.
Yes, it's a pretty good book. Not sure what the siblings are on about.
Used it in college about 8 years ago. Can't say I'd do the same today, but back at that time it made some sense as college programming classes were teaching more than just practical programming knowledge.
I did. It’s short, clear, well written and I found it an excellent introduction to C. I’d recommend it if you read it alongside some more modern supplements.
I have it on the bookshelf but haven't consulted it for many years.

Zed Shaw is sometimes a bit controversial but he wrote a "learn c the hard way" (or something similar) that aims to obsolete K&R.

K&R is a classic. A true exemplar of technical writing.
Damn it, two giant gross "toenail fungus" ads taking up most of the page - shocked me a bit and I closed the page really quickly (and probably now associate this book with fungal diseases - choose your ad providers carefully!)... Is there a non-fungus-y link to this book somewhere?
I was theoretically aware that there are people out there that don't use ad blockers, but I sure as heck didn't expect to find one on hackernews.

I'd recommend uBlock Origin.

That's an ethical question that I've weighed a decent bit. I don't use ad blockers personally, as ads are truly the reason most of the internet is free. If ads are too intrusive, I will 'vote with my clicks' and avoid that particular website in the future.

There are privacy concerns with this, of course, but DuckDuckGo and modern Safari (with cross-site tracking prevention) helps mitigate a bit of it.

"If ads are too intrusive, I will 'vote with my clicks' and avoid that particular website in the future."

They'll never even notice, and in the meantime you're at risk of all sorts of trackers, malware etc. Plus...you're seeing adverts all the time. I used chrome on android the other day and I couldn't believe how many there were, or how annoying. If you're not clicking on them it means nothing anyway, as far as I can tell. I remember back in the day sites saying "click on the ads - it helps us" but I don't think that's a thing any more. Perhaps I should knock up a script to visit random sites with ads and maybe click on a few, in a container/vm. Would that help?

> Perhaps I should knock up a script to visit random sites with ads and maybe click on a few, in a container/vm. Would that help?

Don't worry, there's enough publisher-side fraud already doing that for you!

I felt the same way once, especially when Google first came onto the scene with their plain text unintrusive ads. After the 90's, they truly were a breath of fresh air. They weren't intrusive at all and for the first time were often even relevant. If memory serves I even bought at least one item from a text ad on Google sometime around 2005 or 6.

What's happening today couldn't be more dramatically different. Tracking your activities around the entire Internet, then using your own computing resources to auction them off to the highest bidder for every new page you view.

I don't have a solution to the ethical conundrum. But accepting this kind of thing cannot be it.

I used to use one, but ceased.

Once I started using "request policy", then eventually uMatrix, I found I no longer needed to use an ad blocker.

Especially if one browses with javascript disabled.

I occasionally do occasionally see ads, but now they tend to be non intrusive, and for sites I frequently read, can be avoided w/o the use of a blocker (and the regex engines they tend to depend upon).

As long as he's publishing under a CC license, it might be nice to release the LaTeX source as well. PDF is usually good enough, but sometimes needs to be converted to other formats; you usually get best results going latex->epub than pdf->epub. Some devices it's a lot easier to be able to change attributes to better fit the form factor.
If the source was available, we could fix all the formatting errors.
The Linux Programming Interface: A Linux and UNIX System Programming Handbook - is arguably the best C programming book nowadays if you code in Linux/Unix, the rest C books are most for ANSI C, that might be more useful for bare metal embedded system that does not run a full multi-task POSIX OS?
IMHO, the phrase “C is a compiled programming language” is super confusing.

Edit: there are many languages that have both compilers and interpreters. There are several C interpreters as well. The classification of languages as “interpreted” or “compiled” does not appear to be a sound concept, IMHO.

Why? I guess it's because some interpreted languages are also compiled?

"Compiled" traditionally means "not interpreted", or rather "compiled to machine code".

because languages are specification, "books". they are not interpreted nor compiled. The most common implementations of the C language are compilers, yes, but "compiled" it's not a language property

oh, and btw: there are multiple c interpreters

As another denizen of this chain, I still don't see the confusion at all.

> because languages are specification, "books". they are not interpreted nor compiled

Languages are a specification, and an implementation working together in perfect harmony, with absolutely no undefined behaviour at all, yes, keep walking now.

At least... the good ones try to be, cough ignoring small half baked interpreters and compilers I've had the pleasure of working with cough and never touching again.

> The most common implementations of the C language are compilers, yes, but "compiled" it's not a language property

It absolutely is a large part of the C language and worth teaching, and I'd only split hairs in a programming language theory class, this is a book that presumably leaves the reader with a better grasp of C than before. And arguing against it is an exercise in personal experience I assume (you have C interpreter experience I wager?).

Most people are still taught C in terms of a compiler like gcc, or clang. Source code in, object code (for a specific language specification, target architecture, etc) out. Think operating systems, kernel modules, executables, dll's, and, etc.

I never touched a C interpreter (but I am curious at such a beast), but I know that C is fine to be referenced as "compiled".

so, is javascript an interpreted language? what about jits? ...or babel, for what is worth.

if languages are "compiled" why does both C and C++ (and also java etc...) need a "memory model"? bare physical address and real threads should be enough, no?

> Languages are a specification, and an implementation working together in perfect harmony, with absolutely no undefined behaviour at all, yes, keep walking now.

never talked about "quality" or "comprehensiveness" of the specs. Just that languages are specs, not implementations.

Yes, in the real world you will use gcc, and you will learn that you write some text stuff and it will transform to an executable for you.

Still, why don't you use the right terminology?

The problem is that, if a book slip on this -- so basic -- definition, how good can it be on the complex parts?

The root thing here is the author said "C is a compiled programming language". According to the objection, C is also, an, interpreted programming language so its not the correct classification. That's the complaint I read...

I just don't see this complaint as valid. From the discussion garnered here, C be both a compiled and interpreted language. This is a book about C in the context of the compiled variant, and has references to gcc and clang. The level of detail and nuance when using terminology here is used to match the context the terminology appears in. Excess verbiage for terminology isn't a panacea to confusion, in fact it can increase it. Can't "C is a compiled language" and "C is a interpreted language" both be true independently?

Don't get me wrong, I like reading perspectives like yours, because I just discovered a bunch about C interpreters.

But I feel like you are asking for a "spec" level document on modern C, when you are reading a more practical work on it.

> because languages are specification

It just so happens that the specification in this case makes very specific demands on the translation environment, closely describing compilation step by step from source files into a program image.

Your "interpreted C" is only C in the loose sense that one may describe other non-compliant but roughly similar implementations.

Why doesn’t the standard just say this explicitly? The last time I looked at it the word “compile” just appeared once in a footnote.
> Why doesn’t the standard just say this explicitly?

It does say it explicitly, just using other terminology than "compile" (opting instead for "translate"). See e.g. 5.1.1 in C99.

What about the phrase do you find to be super confusing? It is a short summary (even labeled as takeaway 0.1.2.1 in the text) of the first paragraph of section 1.2. That paragraph explains how C source code is just text and that it is turned into an executable program with a compiler. In context the phrase seems more than clear.
The explanation of what a compiler is and what it does is perfectly fine. A C compiler is how most of us do C. But going to “C is a compiled programming language” is quite the wrong generalization IMHO.
A little off topic, but that is some terrible art on the book cover.
> A little off topic, but that is some terrible art on the book cover.

Relative to other books in the series, or do you object to the "historical fashion" style book covers that publisher tends to use?

egghead.io has some awesome illustration for the courses: https://egghead.io/browse/languages/javascript Technical book publishers should just stop hiring ink illustrator that like medieval drawings and get graphic illustrators that like Adobe Illustrator and SVGs
From the author's page:

> I find such a C library project aiming to be standard .... with a really humane discussion culture, a proof that people (even men!) can constructively work together to achieve great things without denigrating each other.

Is this casual sexism helpful to anyone, men or women?

Whether it's helpful or not, I hope it's self aware. Jumping from denigrating men to celebrating a culture without denigration in a single sentence is impressive.
Why is it always that SJWs like you need to find that one joke, one excerpt and turn it into some kind of personal war/problem/argument/discussion/hate? Instead of focusing on the VALUE brought by the author, you managed to dig up one benign joke and went on to discredit the entire thing. What's worse, you actually put EFFORT in finding something you can use for this purpose.

Why aren't you ashamed of yourself?

> Why is it always that SJWs like you need to find that one joke, one excerpt and turn it into some kind of personal war/problem/argument/discussion/hate?

Namecalling ("SJWs like you") is against the hacker news guidelines. I would suggest you read them.

Also, it's worth noting that actual, self-proclaimed SJWs (I am one, I know many, and am in their communities) wouldn't have any problem with the joke in question, because it's "punching up", i.e. it is making a joke about social problems in a group that has the systemic advantage. So your name calling isn't even accurate in this case.

> Instead of focusing on the VALUE brought by the author, you managed to dig up one benign joke and went on to discredit the entire thing.

Please point out where the poster "discredit[ed] the entire thing". I don't see them discrediting the entire book, I see them questioning a bit of context around the joke, which is a perfectly valid thing to do.

> Why aren't you ashamed of yourself?

Why aren't you? The poster is asking a legitimate question around the context of a line of the book, and you're insulting them and denigrating them for this.

> Namecalling ("SJWs like you") is against the hacker news guidelines

But it's not against common sense to drive focus from what's important to what's personal, right? It bothers you I wrote someone's a social justice warrior? It's not name calling, it's a simple, factually correct observation. A person comes along, reads the content, decides to fight in the name of all men, completely disregarding the contribution that the post is about, deliberately missing out on a clear JOKE. I don't have to draw it for you, right?

> Also, it's worth noting that actual, self-proclaimed SJWs (I know many and am in their communities) wouldn't have any problem with the joke in question

Which implies that you know EVERY "SJW" because you know a few, therefore you're expert in the field? Let's not play this game. It's an insult to both our intellects. Again, I'm not name calling, it's a fact. I'm a person with access to broadband internet, I've seen way too many members like this one - or you for that matter - who are focused on bashing and discrediting and being toxic in general. There's no greater good coming out of this pointless discussion.

> I see them questioning a bit of context around the joke, which is a perfectly valid thing to do.

No, it's not valid. That's the bit you don't get. That's EXACTLY what's wrong.

> Why aren't you? The poster is asking a legitimate question around the context of a line of the book, and you're insulting them and denigrating them for this.

I'm not insulting anyone, but you did take the opportunity to play that person's lawyer. Does my existence offend you by any chance? I'm a human being entitled to an opinion (just like the SJW in question is), I felt like voicing it after witnessing a toxic person online. You seem like another one. Would you, given the opportunity, shut me up because you - obviously - dislike what I've to say about someone with ill intentions?

Again, it's not an insult. It's an observation. Don't turn everything into insults, it's a common way to defend your faulty opinion and justify your own toxicity.

I'm sure you'll have a lot to dissect and post, perhaps to satisfy your own ego or to impress other readers, it's irrelevant - I just wanted to say that I won't read it so if you're a smart individual - you won't bother straightening me out. Have an excellent day, may you focus on learning instead of fighting other people's battles.

> a bit of context around the joke, which is a perfectly valid thing to do.

It is not.

The poster insinuated that there was "casual sexism"

It makes people feel uncomfortable.

It's micro aggression.

I have a right to read the comments section without being forced to read aggressive comments out of context.

Please, be more careful next time.

Maybe this is more about outrage culture (being upset over relatively small things like microagressions rather than "real problems", often argued to be a fallacy of relative privation, unless the small things are not a problem at all or the argument is that the response is disproportional to the magnitude of the problem) than about whether an SJW is of a feminist or some other persuasion. Some disagree that SJWs are necessarily feminist or vice versa. A lot of these terms aren't exactly clearly defined.

The argument could have been a lot clearer and less confrontational. At least it seems more consistent than anti-feminists being "reactionary" for the old times where we could make jokes about women but being offended by jokes about men.

> Why aren't you ashamed of yourself?

Regardless of the argument, that's an unkind, personally-attacking tone and doesn't seem necessary to your criticism.

It's a simple question, nothing else. You're free to interpret it however you want. Why are you focused on how much I maybe "hurt" the other toxic person and not on the fact that we've a creator being bashed by a random person with access to broadband internet about something that's intended for comedic effect?
This is not productive. I have opinions about the tone here because I like reading comment threads here. "Hurt" is not something I can speak to.
> SJWs like you

That's awful lot to infer about someone from one sentence.

> turn it into some kind of personal war/problem/argument/discussion/hate

Picking the "discussion" part out of that, why is discussion on these topics a bad thing?

> one benign joke

Benign is subjective.

> discredit the entire thing

I think a more charitable read would not result in the implication that the question was intended to discredit the entire work.

> you actually put EFFORT in finding something

Or perhaps the parent just saw this when reading the page.

> Why aren't you ashamed of yourself?

Probably because it was just a simple question being asked.

> Benign is subjective.

It really isn't. Unless you lack sufficient processing resources in your biological central processing unit that prevents you from inferring the intent. In that case, you've a much bigger problem :)

> Or perhaps the parent just saw this when reading the page.

Oh please, spare me. A parent went crazy after reading a joke in on a site related to C, better bash the author, he'll break my child who reads programming related sites every day! ignores the rest of the internet, which totally isn't full of violence or inappropriate content Are we going to wage internet war against programmers, specifically ones working with C, because they're damaging our children?

Are you serious? I'm genuinely laughing out loud, you're literally trying to find every iota of wrong so we can have this pointless conversation we'll both forget and all you came up with was - "Or perhaps the parent just saw this when reading the page" :)

I'll screenshot this, this is gold!

> Probably because it was just a simple question being asked.

Simple question answered with another simple question.

To clarify, by parent I was referring to the original commenter who made the parent comment to yours. My comment had nothing to do with biological parenthood. I'm glad you had a good laugh :)
The project is a testament to how women, who are naturally irrational, can nonetheless contribute to a software project, especially in the presence of men who in nature are found alone and never in groups.
You pathetic, pompous millenial cunt. Shut. the. fuck. up.
> Is this casual sexism helpful to anyone, men or women?

It communicates the author's perspective to the reader, and that may be useful to them if they believe (correctly?) that such a statement will bring them favour.

Personally, I dislike it and hold a dim view of it; but I can't speak for others.

Personally, I'm starting to find it hilarious just how bent out of shape people get over even the most minute poke at the male ego, particularly when so many of them would probably tell anyone else offended by anything else to stop being such politically correct SJW babies.

Of course, even in a thread about C, this would be the top conversation. A minor, insignificant, barely even snarky two word parenthetical. Of course.

I guess outrage culture is only a problem when someone other than a man is outraged...

Weird. My first thought reading that even men! parenthetical was not that the author actually had a dim view of men.

If the parenthetical read "even women!" that would not be perceived as a minute poke at the female ego. Commentary would be at least an order of magnitude louder/more inflamed. You know damn well there would never be an "even women!" parenthetical by any author who cared about their career.

So it's weird for you to take what little commentary there is and shoehorn it into 'minute poke at the male ego'. Honestly I paid way more attention to it due to your comment, otherwise it would have just slid out of my awareness.

Exactly this. How many people who are commenting are missing the point is hilarious.

Fwiw, my take is that outrage culture has gone too far... maybe its time to play their game.

>If the parenthetical read "even women!" that would not be perceived as a minute poke at the female ego. Commentary would be at least an order of magnitude louder/more inflamed.

Come now. Far worse things are said about women all over the web all the time.

And the point is the double standard. When women are outraged about toxic male behavior that leads to sexual harassment, violence, rape culture, keeps them out of employment in certain fields, etc, they're derided and dismissed as radical feminist man-hating social justice warriors engaged in mob outrage and misogynistic witch-hunts.

When men are outraged about literally anything, no matter how trivial (and this is very trivial), it's always perfectly justified.

> Come now. Far worse things are said about women all over the web all the time.

You can't possibly be ignorant enough to think that the existence of blatant (albeit throwaway) misogyny like the example here would be tolerated on HN? There'd be rightful complaints right at the top of the thread and dissenters would be downvoted to hell. To be clear, I'd very much be joining in on the downvoting: a throwaway joke about women being bad at something isn't within the bounds of what I consider acceptable in eg learning materials.

Believe it or not, there are people out there who were raised with a shred of moral consistency, and who have actual moral beliefs (like being against sexism) instead of whatever grotesque facsimile of a conscience you've convinced yourself is your moral center. "But there are people out there who do worse!" isn't an ethical argument, it's a toddler's excuse for acting out.

How can we end toxic masculinity if we cannot tolerate male fragility?

Men expressing vulnerability and anguish when hurt ought to be lauded, not laughed at or dirided.

I looked at that book, but I was worried about the Gtk part, the book is now a few years old, is the Gtk stuff still relevant? My worry is that Gtk seems to always be coming out with new versions and its hard to know whats still relevant and good.

Is that a valid concern or not?

This isn't vulnerability and anguish, it's petty nitpicking and virtue signaling.
That sounds like irony. Given the author is German and lives in France for years, it could totally be a sarcasm to denounce the current situation. It's hard to tell just by text though.
I can only beg that you lighten up a bit - after all it's men that have caused and fought most wars, etc etc.. the author is a man poking lightly at the tragedy of man's ego. Do you really need a list of examples to verify this?

Anyway... lighten up.

- a Man

Scandalous language on page 3!

> printf("element␣%zu␣is␣%g,␣\tits␣square␣is␣%g\n",

Nice, any news on an update on C Programming: A Modern Approach?

Because I liked that book even more :-) Thanks

How does this book compare with "21st Century C"?

I know that, unlike "Modern C", "21st Century C" can't possibly cover C17 because that book was released in 2014.

But otherwise, what would be notable differences? In terms of style, correctness, idiom, depth, and breadth?

"Modern C" is a beginners book.

"21st Century C" is for people who have learned C in the past but need to brush up their knowledge with modern practices.

As someone who learned C at the time of the first edition of the K&R, "21st Century C" would be more useful if I ever had to code in C again (which hopefully I won't). A refresh, 5 years later, would be useful, though.

love it, thank you for giving the PDF out for free
(comment deleted)
I'd love a language that feels like a more convenient C. C as it is, with some niceties like built-in containers with some nice syntax sugar. Maybe someday Zig will get there.
Why would conservative use of C++ not meet this mark? It's highly compatible with C, has container types, ranged for-loops, operator overloading which makes use of container types more sugary.
"Conservative use" introduces a massive cognitive load of trying to figure out what you are and what you aren't using.

And the ergonomics of modern C++ look absolutely horrid compared to actual modern languages.

How is it a massive cognitive load? Use clang libraries and error out if there are any operator declarations, for instance.
"How is it a massive cognitive load? Just write a bunch of tooling to enforce your rules!"
It's simpler than the tutorial example for python-clang. It's strictly less work than a responsible unit test suite, and there's not much downside to getting some details wrong on this particular application.
>tutorial example for python-clang

Do you have a link? Your comment is the highest Google result for that quote.

You can just use features of C++ which improve the ergonomics of C; it's not necessary to buy into modern C++ wholesale. If you want to write a C program but with generic container types, just use the features of C++ which facilitate that.

> And the ergonomics of modern C++ look absolutely horrid compared to actual modern languages.

True, but if you want to write "C as it is, with some niceties", then you're not talking about modern languages.

I disagree heavily with that. Templates, classes, move semantics and destructors make for very tight and elegant programs I have found. No inheritance.
Have you looked at Crystal language? Its slogan is fast as C, slick as Ruby
D is pretty darn nice and you can write code as if it were a C project, you don't have to use classes if you don't need them. I also view Go as very C-like honestly. People complain about things missing in Go that are technically not in C either, except it has a lot more batteries included stuff out of the box.
And preferably part of Clang and GCC collection...
A language called clay was basically this. With better marketing and follow through it could have taken off. It had templates and move semantics and was interchangeable with C. The author was using it to write parts of a program that was already written in C.