If you keep track of which boxes are and are not runtime memory cells, that should be enough to work out any particular C pointer problem except the pointer-array almost-equivalence mess.
My understanding of types took a big step forward when I read some of Robert Harper's stuff. In particular, the blog post, Dynamic Languages are Static Languages, and his book, Practical Foundations for Programming Languages. (The book is a tome and I've only read parts of it but it's very good).
When it comes to understanding memory in C, another important aspect is understanding how linkers and loaders work. Also, it's good to know something about calling conventions.
Go: Basically the same as C, but with better specification for type sizes, more rigid rules about automatic type conversion, no pointer arithmetic (you can do it using the unsafe package but it is highly discouraged by both the language design and idiomatic usage) and a compiler which can do type inference.
Also, when you get to manually allocated heap data (which this article doesn't cover) you don't have to worry about deallocations... usually.
Variables? What state? Everything is puuuuuuuuure.
In Python:
Everything is an object (numbers, true/false values, strings, etc), some are mutable and some are not. Variables are temporary labels on objects (think of them as hard links).
In Rust/C++:
There are various types of boxes / smart pointers (shared, unique, heap, etc), and unsafe / raw pointers should be avoided when possible.
In C:
Not every variable has a data type, e.g. void or function pointers.
"Not every variable has a data type, e.g. void or function pointers."
A void pointer has type "void* "; a function pointer also has some appropriate type.
Not every object has a type (e.g., a chunk of memory allocated by `malloc()`), but if "variable" means "object created by a declaration", then yes, every object has a type.
As the lab TA for a first year course in Java, I don't know how many times I repeated "A variable is like a box: it has a label (the variable name), and it stores something." - it's a simplistic analogy, but not far wrong (at least for Java), and it helps the new programmers get the idea.
If you keep track of which boxes are and are not runtime memory cells, that should be enough to work out any particular C pointer problem except the pointer-array almost-equivalence mess.