C is pass-by-value which means that all the parameters are copied to the stack frame every time you call a function. C also does not support inner functions that could access/modify local variables in the lexically enclosing function, although GNU C does as an extension. The only way to 'pass by reference' in C is to pass a pointer. While there are reference types in C++ these are just pointers behind the scenes.
I thought about the following scenario in a hypothetical low-level C-like language. There could be a function declared inside another function only visible to the outer function and will only be called by the outer function. If my understanding is correct the stack pointer is incremented by a fixed amount when the inner function is called assuming no funny business such as variable-sized objects. Therefore as the thought process goes any local variables declared in the outer function would be at fixed offsets relative to the stack pointer even inside the inner function. They could in theory be accessed in the same way as other local variables without needing to pass a pointer. And of course trying to access and call the inner function from outside the the outer function would try to access out-of-lifetime local variables in the outer function and result in undefined behavior.
Here is an illustration: (again this not C but an imaginary C-like language)
void (*address_to_inner)(void);
void outer(void) {
int a = 10;
void inner(void) {
printf("%d\n", a);
}
inner(); // Prints 10
address_to_inner = inner; // Not undefined behavior yet but not good
}
void another(void) {
outer(); // Prints 10
address_to_inner(); // Undefined behavior because inner() tries to access `a` after it was deallocated
}
Does my thought process apply on typical architectures such as x86 or ARM? Some features such as automatic memory management are not supported in low-level languages such as C because they are not natively supported by architectures and would require too much complex behind-the-scenes work to be feasible for a low-level language. But is my scenario 'feasible' for a low-level language or are there any architectural or technical limitations that I did not consider that would make implementing this non-trivial based on how the stack/local variables and function calls work? If not why not?