This behaviour is not guaranteed by the C++ standard - it doesn't even mention the stack or heap.
But typically, when you call a function, a stack frame is pushed on to the stack that is large enough to contain all of that function's automatic variables (and other objects, such as its return value). So if you consider the function foo:
void foo() { int x; std::string str; }
When this function is called, the top of the stack is pushed up so there is enough space for an int and a std::string. The size of those types are implementation-defined, with some restrictions placed on them by the standard, but you can just think of them as sizeof(int) and sizeof(std::string) bytes.
Now when you have an array in your function, such as int a[10], the stack frame for the function will contain enough space for 10 ints, or 10*sizeof(int) bytes. This frame size is baked right into your executable - when the function is called, the stack will increase by that size.
When you do dynamic allocation, such as int* a = new int[10], you are allocating space for 10 ints, or 10*sizeof(int), in the heap. However, you have increased the stack frame by some amount, namely sizeof(int*). The pointer object itself is stored on the stack but the ints that it points to are on the heap.
Note that in the first example, you may wonder how the stack frame size could be baked into the executable if an std::string can have variable length. That's because the std::string object itself has a fixed size, sizeof(std::string), but most likely does some kind of dynamic allocation to manage its internal representation - this internal representation will be on the heap.