I have a report, unconfirmed by me but from a reliable source, that the code
qsort(a, n, sizeof *a, cmpfunc);
is compiled by a modern version of gcc as if it had been written
if(n == 0)
__builtin_trap();
qsort(a, n, sizeof *a, cmpfunc);
Evidently it is believed that calling qsort
with n == 0
is undefined behavior.
[Edit: The whole premise here was found to be false; see "Update 2" below.]
It has been pointed out that Posix explicitly blesses the n == 0
case, but evidently no extant version of the C Standard does.
So the obvious questions are:
- Is calling
qsort
withn = 0
actually undefined behavior in C? - Is every program which ever calls
qsort
with arbitraryn
truly obliged to check forn == 0
and not callqsort
in that case? - Why would gcc perform this "optimization"? Even if you believe that calling
qsort
withn == 0
is undefined, this would seem to marginally slow down every non undefined program.
Textbook implementations of quicksort (which, I know, qsort
is not required to be) pretty much can't not handle n = 0
correctly. I wonder if gcc's behavior here is trying to guard against a qsort
implementation that somehow does something much worse than a __builtin_trap
if the initial call has n == 0
?
Update: Thanks for the responses so far. It sounds like gcc is in the wrong here. As I said, I haven't confirmed this result myself, but I'm trying to find out which version of gcc and with which optimization flags the issue was observed.
Update 2: The original report I referred to was in error. Two key clarifications:
- gcc was in fact checking for
a == 0
, notn == 0
. This is obviously a completely different kettle of fish: As this thread (and others) has confirmed, callingqsort
on a null pointer is considerably more problematic, and almost certainly formally undefined. - The compilation in question included the
-fsanitize=undefined
and-fsanitize-undefined-trap-on-error
flags, so of course gcc was being stringent about checking for inadvertent null pointers (and even at a cost to efficiency).
Sorry for the misinformation and runaround. I'm afraid this question is now in the realm of "not reproducible or was caused by a typo", and I have put one close vote in the hopper on that basis.
For what it's worth, the gcc version was 12.2.1.
n
? Recently I used a compiler that silently checked all usages of enumerated values. Anyway, a confirmation would be great, for example as an example at godbolt's Compiler Explorer. – Earedn == 0
case, but evidently no extant version of the C Standard does. C11 7.22.5 Searching and sorting utilities p1: These utilities make use of a comparison function to search or sort arrays of unspecified type. Where an argument declared assize_t nmemb
specifies the length of the array for a function,nmemb
can have the value zero on a call to that function; the comparison function is not called, a search finds no matching element, and sorting performs no rearrangement. – Emergencyqsort
calls thecompare
routine one or more times". However it also says "ifwidth
is less than zero, the invalid parameter handler is invoked" – Tutelageif (nlinks > 1) qsort(…);
(instead of the proposedif (nlinks != 0) qsort(…);
) — there is no need to sort a 1-element array (or a 0-element array, especially not one identified by a null pointer). – Gambadoif(n == 0)
withif(a == NULL)
for the corrected reading. See mm.icann.org/pipermail/tz/2022-October/032107.html – Banking