size_t array_len = SIZE_MAX;
int *my_array = calloc(array_len, sizeof(int));
int compar(const void *n_to_search, const void *arr_element)
{
if(*(int *)n_to_search > *(int *)arr_element)
return 1;
else if(*(int *)n_to_search < *(int *)arr_element)
return -1;
else
return 0;
}
void search(int n_to_search, size_t *idx)
{
int *result = bsearch(&n_to_search, my_array, array_len, sizeof(int), compar);
if(result != NULL)
// Is this operation valid according to c99 standard?
*idx = result - my_array;
}
In this case result
is guaranteed to be greater than or equal to my_array
, so the result of the subtraction will always be >=0
, furthermore it is guaranteed that result - my_array < SIZE_MAX
since bsearch()
takes array_len
as parameter, so the search will only happen between the first array_len
elements of my_array
.
So my question is: can code save the result of the subtraction in a variable of type size_t
without ending up in an UB according to the c99 standard?
Can code use
size_t
instead ofptrdiff_t
when subtracting two pointers where the minuend >= subtrahend and the result is guaranteed to be <SIZE_MAX
?
Yes when PTRDIFF_MAX >= SIZE_MAX
or the result is guaranteed to be <= PTRDIFF_MAX
, else code risks UB.
In the case of a large allocations, we can have issues:
int *my_array = calloc(array_len, sizeof(int))
may return NULL
in which case the search is moot. So let us assume my_array != NULL
.
bsearch()
implementation may be weak and fail and return an invalid pointer. Let us assume it can handle searches of memory that is more than SIZE_MAX
bytes and return a correct pointer. This pointer must be NULL
or >= my_array
.
We are then left with the subtraction result - my_array
which returns a type of ptrdiff_t
, "which is the signed integer type of the result of subtracting two pointers;" (C23dr § 7.21 3).
ptrdiff_t
wider than size_t
:
When PTRDIFF_MAX >= SIZE_MAX
, there is no issue in converting the signed difference of result - my_array
to an size_t
. The difference is always 0 or positive.
ptrdiff_t
same width as size_t
:
When PTRDIFF_MAX < SIZE_MAX
, (e.g. PTRDIFF_MAX == SIZE_MAX/2
) we risk undefined_behavior (UB) due to:
When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is
ptrdiff_t
defined in the<stddef.h>
header. If the result is not representable in an object of that type, the behavior is undefined. C23dr § 6.5.7 10
J.2 Undefined behavior
The result of subtracting two pointers is not representable in an object of typeptrdiff_t
(C23dr J.2. 6.5.7).
The UB may or may not be tolerable (e.g. the subtraction "wrapped" in which case the following conversion to size_t
wraps to the desired index) - yet it is still UB.
Code attempting calloc(a, b)
and subsequent code for that pointer where a*b
exceeds minimum(SIZE_MAX, PTRDIFF_MAX)
risks portability issues as here be dragons.