Ваш компилятор обнаруживает и предупреждает вас о том, что вы неявно меняете свою интерпретацию байтов, хранящихся в области, на которую указывает s
. Прототип функции указывает s
как указатель на char
, и по умолчанию в вашей настройке char
кажется подписанным. Однако, чтобы получить правильную арифметику, вам нужно использовать только беззнаковые значения. Итак, вопрос в следующем: что должен делать компилятор со значениями, указанными через s
, которые на самом деле имеют отрицательные значения?
Давайте быстро отвлечемся, чтобы убедиться, что мы понимаем, какие ценности мы рассматриваем. Возможные значения для signed char
- от CHAR_MIN
до CHAR_MAX
включительно. (Эти значения можно найти в limits.h
.) Возможные значения для unsigned char
: от 0
до UCHAR_MAX
включительно. Таким образом, возникает вопрос: как мы представляем возможный диапазон значений от CHAR_MIN
до CHAR_MAX
в пределах диапазона 0
до UCHAR_MAX
?
Один простой подход - просто позволить компилятору выполнить это преобразование за вас: он просто использует арифметику с циклическим изменением, чтобы убедиться, что значение находится в пределах: он автоматически добавляет UCHAR_MAX + 1
достаточное количество раз, чтобы получить значение, которое находится в пределах диапазон от 0
до UCHAR_MAX
. Однако фактическое значение этого параметра может зависеть от используемого вами компилятора. Именно эта возможность непереносимости лежит в основе предупреждения вашего компилятора.
Хорошо, так откуда это нас? Что ж, если вы готовы взять на себя ответственность за гипотетические проблемы переносимости, которые вызовет этот подход, вы можете сказать компилятору, что вы рады, что он выполнил преобразование с использованием стандартных правил. Вы делаете это с помощью cast :
hashval = ((unsigned char) *s) + 31 * hashval;
Этот подход будет подавлять предупреждение и гарантировать, что ваша арифметика будет выполнена как беззнаковая, что вы и хотите для такого рода функции. Однако вы должны знать, что один и тот же код в других системах может давать разные результаты хеширования.
Альтернативный подход состоит в том, чтобы использовать тот факт, что стандарт ANSI C указывает, что указатели можно корректно приводить к типу unsigned char *
для доступа к базовой структуре байтов данных, на которые указывают. (В данный момент у меня нет моей копии стандарта, или я бы дал вам ссылку.) Это позволит вам обобщить этот подход для создания функции, которая дает вам хэш-значение любого значения данных. тип. (Однако для этого вам нужно подумать о том, как вы знаете размер передаваемых данных.) Это может выглядеть примерно так:
unsigned hash(void *s, size_t n) {
unsigned char *t = (unsigned char *) s;
while (n--)
hashval = (*(t++) + 31 * hashval) % HASHSIZE;
return hashval;
}
Надеюсь, это даст вам некоторое представление о том, что происходит.