the per-thread stack versus shared memory
When several threads run in one program, the natural question is: what do they share, and what is private? The clean answer is that each thread gets its own stack, while the heap, the global variables, and the program code are shared by all of them. So a local variable inside a function is private to whichever thread is running that function, but anything reached through the heap or a global is common ground that every thread can see and change.
Recall that the stack is the region of memory holding a function's local variables, its arguments, and the return address for getting back to the caller; it grows and shrinks as calls happen and return. Because each thread follows its own chain of calls, each thread needs its own stack - so the operating system hands every new thread a fresh stack region of its own. Local variables therefore live on a per-thread stack and are naturally private: thread A's local int counter and thread B's local int counter are two different boxes at two different addresses. The heap, by contrast, is one shared pool: a block from malloc(), or any global or static variable, lives at one address that every thread sees. If two threads hold a pointer to the same heap object, they are aimed at the very same bytes.
Why this matters for safety: the private stack is automatically safe - no other thread can reach your locals, so locals never need locking. The danger is the shared region. The moment two threads can reach the same global or the same heap object and at least one of them writes, you have shared mutable state, and you must coordinate them or risk corruption. A practical rule of thumb falls out of this: keep data on the stack and local when you can; treat every shared heap object and global as something that needs a plan. And never pass a pointer to one thread's local variable into another thread that outlives the function - when that function returns, the stack frame is reclaimed and the pointer dangles.
int total = 0; (a global) is one box every thread shares - dangerous to write from many threads. Inside a thread function, int i = 0; is a fresh box on that thread's own stack each time - safe, because no other thread can reach it.
Stack locals are private and safe; the shared heap and globals are where the hazards live.
Each thread's stack is smaller and fixed-size (often a few megabytes), so deep recursion or huge local arrays can overflow one thread's stack even when plenty of total memory is free. The shared heap is the place for large or shared data.