Getting Started With the Classic C Programming Guide
If you are trying to actually understand how C works instead of just copying snippets from Stack Overflow, Peter Van Der Linden's book is still one of the better resources out there. It has been around since the mid-90s and the third edition came out in 2001, which means some of the examples feel dated but the core material is still solid. The book is structured in two acts. The first half walks you through Standard ML, a functional language that forces you to think about recursion, pattern matching, and immutability before you ever touch a pointer or a malloc call. The second half pivots to C, where those same concepts get translated into the messy, manual world of memory management and undefined behavior. The transition is deliberate and it actually works if you sit with it.
Language Of Composition 3rd Edition
The full title is just "Programming in Standard ML '97" for the first section, but everyone refers to the combined work as the Language of Composition book. It was designed to teach you how to think like a systems programmer by starting from a clean functional foundation and then deliberately stripping away the safety nets one by one. You learn what happens when you take away the garbage collector. You learn why pointers are dangerous. You learn what stack overflow actually means in practice because you caused it yourself in SML first. There is a reason this approach exists. Most C tutorials throw array indexing and pointer arithmetic at you on day one. This book makes you write a merge sort in SML before you ever see a C array. By the time you get to the C chapters, the concept of a linked list or a recursive traversal is already familiar. You are learning syntax, not fundamentals.
What the Book Actually Covers
The SML portion covers the basics of the language: data types, functions, pattern matching, list processing, and recursion. It also introduces SML/NJ as the reference implementation. The exercises are intentionally painful. You will spend time on the book's problem set that feels disproportionate to the payoff, but that is the point. The friction is the pedagogy. Once you move into the C sections, the scope expands to include pointers, structs, dynamic memory allocation, file I/O, and the preprocessor. The later chapters get into compiler construction and a surprisingly deep chapter on assembly language. There is also a notorious section called "Deep C Secrets" which dissects common pitfalls and misconceptions about C semantics. That section alone is worth the price of admission for anyone who has spent time debugging segmentation faults without understanding why they happened.
Get the Full Details

How to Use It Without Losing Your Mind
Do not read this book cover to cover in sequence and expect it to stick. Work through the SML chapters actively. Type every example. Break them. The exercises in the first half are where most people quit because the problems require you to sit with a concept for longer than you are comfortable with. I ran into this myself when working through the section on higher-order functions and list folds. The book assumes you can derive the solution from first principles, which is fine until you cannot and you need a concrete hint. The workaround I ended up using was to write out the type signatures of every function on paper before touching the keyboard. It sounds tedious but it cut my debugging time for those chapters down significantly. When you hit the C portion, expect the pace to quicken. The book moves faster through C material because it assumes the thinking part is already done. Still, do not skim the chapters on pointers and memory. Those are where people who claim to know C but cannot explain the difference between a pointer to an array and an array of pointers live and die by their ignorance in production. The "Deep C Secrets" section at the back is not a summary. It is a collection of edge cases and language specification subtleties that most tutorial books ignore entirely. Things like how the comma operator actually works in a for loop initialization, or why certain array decays behave differently than you expect. Read it after you have some real debugging experience under your belt. Otherwise it reads like a gotcha list and you will miss the underlying principle.
Where the Book Falls Short
It is old. The C standard it targets is C89/C90, not C99 or later. If you are writing code that uses C99 features like variable-length arrays, the C11 _Generic keyword, or even just // comments, the book will not address them. That does not make the content wrong, but it does mean you will need to supplement it if you are working in a modern codebase. The SML portions assume access to SML/NJ, which is free but not installed anywhere by default on modern systems. You will need to set up a local environment or use an online REPL. The book mentions this but does not provide troubleshooting for contemporary installation issues. I had to spend an afternoon getting SML/NJ to compile on a recent Linux distro because of path resolution issues that the book simply does not account for. If you run into that, check the SML/NJ documentation directly rather than flipping back through the book's setup chapter. The book also does not cover build systems, package management, or any aspect of real project workflow. It teaches you C as a language, not as a tool you use in a team. If your goal is to be employable writing C code, you will need to pair this with practical experience on actual projects. No book from 2001 is going to teach you how to manage a multi-file C project with Make or CMake.
Where to Find It
The book is available through Amazon and other major booksellers in both paperback and Kindle formats. The publisher is Springer. It is not freely distributed legally, though you will find used copies circulating at significantly reduced prices if you check marketplace listings. If you already own a copy, the PDF is widely shared in academic circles but I am not going to link to any of those. The effort of finding a legal copy is minor compared to the value of the material. For the exercises, there is a solutions manual available separately. Some people use it as a crutch. Others use it as a checkpoint after attempting the problem. The problems are difficult enough that looking at a solution too early will give you a false sense of competence. Attempt the problem, struggle with it for a reasonable amount of time, and then consult the manual only if you are genuinely stuck.

A Practical Observation
One thing the book gets right that most others miss is the emphasis on writing code that you can reason about. The SML chapters train you to prefer immutable data and recursive decomposition over mutable state and loops. When you transition to C, you already have an instinct for when a mutable variable is creating more problems than it solves. That instinct does not come from memorizing C syntax. It comes from having written enough recursive functions in a language that forces you to think that way. I once spent roughly three days debugging a C program where a linked list implementation was silently corrupting memory because I was modifying a pointer in place instead of returning a new head node. The bug traced back to a habit I had picked up from writing C code before I read this book. After going through the SML sections, I started structuring my list operations differently and that class of bug essentially disappeared from my work. That is a specific, tangible outcome from a book that is twenty-five years old.