When calling a function, whether it is main or any other function, its return address is pushed on the stack.
The important thing to note in the example is:
ret = (int *)&ret + 2;
Here, we are casting ret as an int, and then moving up the stack by &ret + 2 (+14 bytes)
This means that ret is now pointing to the return address for function main.
(*ret) = (int)shellcode;
Here, we overwrite the return address with the address of the shellcode.
So now, when the program returns, the jmp instruction goes to the address of the shellcode and not the intended return address