Everything is pass by value in C, but pointers are values. Functions can't modify the value of a pointer, but they can modify whatever's in the memory pointed to by the pointer. To change the pointer itself, you'd have to pass a pointer to a pointer. Functions like asprintf do this.
C++ added pass by reference (&), which can be a handy bit of syntactic sugar.
"so unless the function declares that it takes a pointer"
What you're saying is correct in the practical sense. But ggreer's answer is the real technically detailed explanation (everything is passed by value, including pointers to memory. But then you can manipulate that memory you have the pointer to. And that makes it effectively pass-by-reference). Also, C++ adds a language defined pass-by-reference operation... so there's that.