I disagree. malloc() is supposed to return a null pointer to indicate
that the requested memory can't be allocated. If it returns a
non-null pointer, it's non-conforming.
What we know is that in the *abstract machine* when malloc() returns
non-null an object has been properly created. What an actual
implementation is required to do is a very different and rather more
complex question. Clause 4p6 says
"A conforming hosted implementation shall accept any strictly conforming
program."
"Accept" is the key word here and the standard doesn't define further what
it means. I suggest that it means "not reject" in the sense of saying "I
won't compile this code because it isn't valid C". Consider that a
strictly conforming program can be arbitrarily long and no real-world
compiler is capable of translating every possible strictly conforming
program. The compiler can say "sorry, I can't translate this" in some
fashion, but not "your program is invalid".
5.1.2.3 places requirement on how an implementation must honour the
semantics of the abstract machine. This is based on observable behaviour
notably on I/O and side-effects notably of volatile objects. 5.1.2.3p5
says
"The least requirements on a conforming implementation are:
- At sequence points, volatile objects are stable in the sense that
previous accesses are complete and subsequent accesses have not yet
occurred.
- At program termination, all data written into files shall be identical
to the result that execution of the program according to the abstract
semantics would have produced.
- The input and output dynamics of interactive devices shall
take place as specified in 7.19.3. The intent of these requirements is
that unbuffered or line-buffered output appear as soon as possible, to
ensure that prompting messages actually appear prior to a program
waiting for input."
Note that what is lacking, and what MUST be lacking if there is any chance
of creating a real-world conforming implementation, is any sense that the
implementation must execute the program successfully to completion. All we
know is that IF a sequence point is reached volatile objects are stable,
IF we reach program termination (see 5.1.2.2.3) file output must match the
abstract machine, IF file I/O to interactive devices happens then it
should behave as per the abstract machine.
The standard doesn't guarantee that a conforming implementation will
execute any strictly conforming program to completion (except one
specified in 5.2.4.1), nor does it place any restrictions on how or why
the execution of a program might fail. All that can really be said is that
to the extent that a program does execute it must be consistent with the
abstract machine.
So, aborting the execution of a program, except the one specified by the
implementation w.r.t. 5.2.4.1, because the implementation doesn't
have enough memory available to continue the execution, is very much
allowed by the standard; not in the abstract machine but in an
implementation. 5.2.4.1 is interesting in that an overcommitting system
must make sure that it doesn't trip up for this program. Maybe. Any
multitasking system that doesn't reserve memory permanently for the
possible translation and execution of this program may find itself unable
unable to do so in some circumstances.
Consider:
void foo(void)
{
char data[1000];
puts("I got here");
data[500] = 0;
}
Let's say an implementation aborted the execution of the program at the
statement data[500] = 0; due to hitting a stack quota limit. It spotted
this when the write operation caused a trap on an unmapped memory page and
it tried to allocate a new one. As far as the abstract machine is
concerned the definition of data causes that object to be fully created
when the block is entered, in much the same way that malloc() has created
an object when it returns non-null (for a non-zero argument). So this
is a non-conforming implementation if you consider overcommitting for
malloc'd memory non-conforming.
Programs can always be affected by external interactions (shutting off
the power if nothing else). I suppose it could be argued that
allowing the program not to finish is non-conforming, but I'm not
*quite* that picky.
But is the standard? I don't see anything to suggest that at all.
The standard allows implementations to impose limits.
For specific things, none of which are directly relevant here.
No realistic implementation can provide unlimited resources (say, for
infinitely deep recursion or a loop that will take a trillion years to
complete). A realistic implementation can provide a malloc() that
doesn't lie to the client.
The question here is conformance. Can a conforming implementation
overcommit or not?
Lawrence