St.Antario
St.Antario

Reputation: 27425

Restricting the size of data structure being created

My header file is

typedef struct vector vector;

vector * vector_create(size_t size);

The c file:

vector * vector_create(size_t size){
    if(size >= PTRDIFF_MAX)
        return NULL;
    //actual creation logic
}

I need to restrict the size of the vector to be no more than PTRDIFF_MAX since I need to subtract pointers to one vector element from the other. The problem comes when I want to test it

void test_create_vector_too_large(void){
#if SIZE_MAX >= PTRDIFF_MAX + 1
    list * lst = list_create(((size_t) PTRDIFF_MAX) + 1);
    assert(!lst);
#endif
}

This code produce tons of warnings

/home/kjroff/main.c:55:32: warning: integer overflow in preprocessor expression
 #if SIZE_MAX >= PTRDIFF_MAX + 1
                                ^
/home/kjroff/main.c:55:29: warning: the right operand of ">=" changes sign when promoted
 #if SIZE_MAX >= PTRDIFF_MAX + 1

The intention of the #if macro was to check if the upper limit of ptrdiff_t exceeds upper limit of size_t. In case they are the same this test does not make sense. But it does in case PTRDIFF_MAX < SIZE_MAX.

How to write such an assertion correctly?

Upvotes: 0

Views: 52

Answers (2)

chux
chux

Reputation: 154305

How to write such an assertion correctly?

Instead of

#if SIZE_MAX >= PTRDIFF_MAX + 1

Subtract (SIZE_MAX is at least 65535) so no overflow possible.

#if SIZE_MAX - 1 >= PTRDIFF_MAX

or simply use > @pmg

#if SIZE_MAX > PTRDIFF_MAX

Upvotes: 2

Eric Postpischil
Eric Postpischil

Reputation: 223274

The comparison is generally useless, as size_t and ptrdiff_t are typically the same width but size_t is unsigned and ptrdiff_t is signed, so SIZE_MAX is necessarily greater than PTRDIFF_MAX. This is not strictly specified by the C standard, as size_t is merely the type used for size expressions and is, in spite of intent, not explicitly specified to be sufficient to represent the size of any object. Similarly, ptrdiff_t is merely the type used for differences of pointers and is, in spite of intent, not explicitly specified to be sufficient to represent the differences of all pointers.

That said, you can change the comparison to SIZE_MAX > PTRDIFF_MAX, which is equivalent, as pointed out by pmg. Another way to quiet the warnings is to change 1 to 1u, yielding SIZE_MAX > PTRDIFF_MAX + 1u. Per C 2018 6.10.1 4, in preprocessing, the integer types behave as if they were intmax_t and uintmax_t, so 1u has the type uintmax_t, so PTRDIFF_MAX will be converted to uintmax_t, then added to 1u (which will not overflow), and the comparison will be between two uintmax_t types (avoiding a warning).

The question states “I need to restrict the size of the vector to be no more than PTRDIFF_MAX since I need to subtract pointers to one vector element from the other.” But the unit of a size_t is one byte, and the unit of a ptrdiff_t is one array element. Unless the vector type is a single byte, the result of subtracting two vector * pointers will be scaled down. Even in constrained systems where size_t struggles to cover all the possible sizes, ptrdiff_t should comfortably have room for the differences of two vector * pointers, regardless of the size of the underlying array. So the test if(size >= PTRDIFF_MAX) seems useless.

Upvotes: 4

Related Questions