References in dynamically typed languages however often make me nervous, as I'm never entirely sure when different types in different languages get copied or not. If I the function argument p gets modified here, did the original variable in the caller get modified? Do I have to write an elobarate check for its mutability?
Stuff like pointer aliasing, dangling pointers, pointer arithmetics (it's UB to take a pointer after the 1st byte after the end of the object for instance), NULL pointer dereference and things like that. Debugging those kinds of bugs can be super tricky and time consuming.
Not that pointers are the only source of UB in C, but they're probably some of the most easily encountered.
There's also the whole array->pointer decay thing that can be a bit tricky to handle in certain cases (I think the sizeof() a parameter declared as an array is probably a surprise to all C coders when they encounter it for the first time).
I think this is mostly an artifact of C's type system. I wish pointers were taught in a language with similar runtime semantics as C with a more expressive type system. Drawing it out helps a ton, but the way C forces you to embed that structure into code doesn't help.
Now a days, things are more complicated with MMUs and virtual memory concepts but I think the examples in the article are a good basis for anyone wanting to know what happens under the hood.
But I did have a problem with remembering to manage memory clear.