msg.sender is always the address of the entity who called the current contract. When a contract calls another contract the
msg.sender changes. Let me give you some examples.
Assuming we have a EOA (Externally Owned Account) A and contracts B and C. When A calls B the
msg.sender in B is A's public address. If contract B also calls contract C,
msg.sender in contract C is B's (contract) address.
So the address of a user never changes (assuming he uses the same account) but the
msg.sender changes in contracts based on which entity initiated the call to that contract.
There is also
tx.origin which is always A in the aforementioned example (in every contract which is called in the transaction from A), but this should typically not be used.