3

I want to support negative indexing on my list implementation, the way I want to handle this (I know there may be better way to handle negative indexing) is by converting negative indexes into their positive equivalent by adding the negative value to the total number of elements in the list.

So if I had 12 elements in my list, and I asked for the index -5, I would do 12 + (-5) = 7 so my real index that I would use to retrieve the element would be 7.

I assume some casts are all that's necessary, and I can probably try a bunch of types like ptrdiff_t and such -- but I want to learn how to determine which type is the correct choice to cast to.

// the size of the list (normally something like list->num_nodes)
size_t list_size = 12;

// the int32_t is the index argument given to an indexing function
int32_t index = -5;

// the size_t is the real index that can be passed to my internal
// indexing function that will walk from either list head or tail
// depending on whether the index is closer to start or end.
size_t real_index = 0;

// if the index is less than 0 I want to add it to the list size
// to effectively subtract, otherwise just assign it
if (index < 0) {
    real_index = (list_size + index); // << warning here
} else {
    real_index = (size_t)index;
}

However adding the int32_t index to the the size_t list_size causes gcc warnings:

warning: conversion to ‘long unsigned int’ from ‘int32_t {aka int}’ may change the sign of the result [-Wsign-conversion]

What is the proper way to solve the problem of adding a negative int32_t to an unsigned value like size_t? I assume it is a simple answer like casting to a larger type that handles both size_t and int32_t (int64_t? ptrdiff_t?)... But how do you determine which is the correct type to cast to (if that is the right solution)?

user338717
  • 77
  • 5
  • Strange, this gives me no warning (`-Wall -Wextra -pedantic`). What compiler are you using? – Marco Bonelli Jul 25 '19 at 18:40
  • Try -Wconversion ./test.c:41:29: warning: conversion to ‘size_t {aka long unsigned int}’ from ‘int32_t {aka int}’ may change the sign of the result [-Wsign-conversion] real_index = (list_size + index); // << warning here --- Also I am using gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11) – user338717 Jul 25 '19 at 18:42

1 Answers1

4

You can cast the int32_t to size_t and add it. The arithmetic will work properly; the result of adding a negative value that has been converted to an unsigned value will result in an unsigned value decreased by the original negative value.

Arithmetic with unsigned numbers operates modulo M, where M is one more than the maximum representable value (such as 256 for an 8-bit unsigned char whose maximum value is 255). This includes conversions. So, if we have an unsigned a and a signed and negative b, converting b to the unsigned type yields M + b (observe that, since b is negative, M + b is less than M). Then adding a is mathematically a + M + b, which, modulo M, is a + b.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312