A recent question on SO concerning "Why does allocating a large element on the stack not fail in this specific case?" and a series of other questions concerning "large arrays on the stack" or "stack size limits" made me search for related limits documented in the standard.
I know that the C standard does not specify a "stack" and that it therefore does not define any limits for such a stack. But I wondered up to which SIZE_X
in void foo() { char anArray[SIZE_X]; ... }
the standard guarantees the program to work, and what happened if a program exceeded this SIZE_X
.
I found the following definition, but I'm not sure if this definition is actually a guarantee for a specific supported size of objects with automatic storage duration (cf. this online C11 standard draft):
5.2.4.1 Translation limits
(1) The implementation shall be able to translate and execute at least one program that contains at least one instance of every one of the following limits:
...
65535 bytes in an object (in a hosted environment only)
Does this mean that an implementation must support a value up to 65535
for SIZE_X
in a function like void foo() { char anArray[SIZE_X]; ... }
and that any value larger than 65535
for SIZE_X
is undefined behaviour?
For the heap, a call to malloc
returning NULL
let's me control the attempt of requesting "too large objects". But how can I control the behaviour of the program if it "requests a too large object with automatic storage duration", specifically if such a maximum size were not documented, e.g. in some limits.h
? So is it possible to write a portable function like checkLimits()
supporting an "entry barrier" like:
int main() {
if(! checkLimits()) {
printf("program execution for sure not supported in this environment.");
return 1;
} else {
printf("might work. wish you good luck!");
}
...
}
An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
It specifies external linkage, and does not technically affect storage duration, but it cannot be used in a definition of an automatic storage duration object, so all extern objects have static or thread durations. In addition, a variable declaration that uses extern and has no initializer is not a definition.
The storage duration of subobjects and reference members is that of their complete object. A name that denotes object, reference, function, type, template, namespace, or value, may have linkage. If a name has linkage, it refers to the same entity as the same name introduced by a declaration in another scope.
Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup. Since its stored value is initialized only once, an object with static storage duration can profile the invocations of a function.
Technically, an implementation only needs to translate and execute one program with a 65,535-byte object (and the other things listed) in order to conform to the standard. It could fail on all others.
To know that larger programs work, you must rely on details of your specific implementation. Most implementations provide for more stack space than 64 KiB, although it may be undocumented. There may be linker switches for adjusting the stack space allowed.
E.g., for the ld
linker on current macOS, the default is 8 MiB, and the -stack_size
switch can be used to set more or less (for the main thread).
I would say that since the C standard says that environmental limits such as stack space may constrain the implementation, anything other than the fact that one particular sample program must work is technically undefined behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With