I think that the thing about C that makes it "like (macro) assembler", for me, has nothing to do with the level of control or the low-level-ness of the feature set, although (a) that helps and (b) I think that it's been eroded a bit, over the years, so that conforming code will run on a wider variety of hardware.
The thing that's special about C (and perhaps Fortran and Forth) is that what happens is what the code says to happen, nothing more. There is very little "magic" about C. The language doesn't insert code for run-time array bounds checks (as in Pascal, Java, most others.) The language doesn't create invisible data structures to support it's object abstractions (VTables and RTT-info in C++). While you can run C code with a conservative garbage collector, like Bohem's, there isn't going to be a magic mechanism moving things around in memory behind your back. (Well, not unless you are using a particularly virulent interpretation of the standard, and something like safec that has decorated pointers of some sort.)
Even the loop constructs are basically just ways to put labels and branches into the code. They don't even constrain the form of the conditional expression, or the variant-update parts. Just labels and branches.
Yes, C does do a small amount of magic. It magically marshalls arguments onto a stack for subroutine calls, and may "promote" some along the way. Yes, it will do a block-copy for you if you do a structure assignment.
The relative lack of magic is the important factor, IMO.
(Pascal's magic level is pretty low, too, so I do agree that it's pretty close to C in this particular spectrum. Range checking is about the limit of magic, here.)