Я не могу сказать вам причину, по которой им пришлось его повторно реализовать, и почему они выбрали int
вместо size_t
в качестве типа возвращаемого значения.Но о функции:
/*
** Compute a string length that is limited to what can be stored in
** lower 30 bits of a 32-bit signed integer.
*/
static int strlen30(const char *z){
const char *z2 = z;
while( *z2 ){ z2++; }
return 0x3fffffff & (int)(z2 - z);
}
Стандартные ссылки на усечение, типы, переполнение
Стандарт гласит (ISO / IEC 14882: 2003 (E)) 3.9.1 Основные типы , 4 .:
Целые числа без знака, объявленные как без знака, должны подчиняться законам арифметики по модулю 2 n , где n - количество битов в представлении значения этого конкретного размера целого числа. 41)
...
41) : это означает, что арифметика без знака не переполняется, поскольку результат, который не может быть представлен результирующимЦелочисленный тип без знака уменьшается по модулю на число, которое на единицу больше наибольшего значения, которое может быть представлено результирующим целочисленным типом без знака
Эта часть стандарта не определяет поведение переполнения для целых чисел со знаком.Если мы посмотрим на 5.Выражения , 5.:
Если во время вычисления выражения результат не определен математически или не находится в диапазоне представляемых значений для его типа, поведение не определено, если толькоВыражение является константным выражением (5.19), в этом случае программа является плохо сформированной.[Примечание: большинство существующих реализаций C ++ игнорируют целочисленные переполнения.Обработка деления на ноль, формирования остатка с использованием делителя нуля и всех исключений с плавающей запятой варьируется в зависимости от машины и обычно настраивается библиотечной функцией.]
Пока что для переполнения.
Что касается вычитания двух указателей на элементы массива, 5.7 Аддитивные операторы , 6.:
Когда вычитаются два указателя на элементы одного и того же объекта массива, результатом является разность индексов двух элементов массива.Тип результата является определенным реализацией знаковым целочисленным типом;этот тип должен быть того же типа, который определен как ptrdiff_t в заголовке (18.1).[...]
Просмотр 18.1 :
Содержимое совпадает с заголовком стандартной библиотеки C stddef.h
Итак, давайте посмотрим на стандарт C (у меня есть только копия C99), 7.17 Общие определения :
- Используемые типыfor size_t и ptrdiff_t не должны иметь целочисленный ранг преобразования выше, чем у целого long со знаком, если только реализация не поддерживает объекты, достаточно большие, чтобы сделать это необходимым.
Никаких дополнительных гарантий относительно ptrdiff_t
,Затем в Приложении E (все еще в ISO / IEC 9899: TC2) дается минимальная величина для длинного целого со знаком, но не максимум:
#define LONG_MAX +2147483647
Каковы максимумы для int
, тип возврата для sqlite - strlen30()
?Давайте пропустим цитату C ++, которая снова направляет нас к стандарту C, и мы увидим в Приложении E C99 минимальный максимум для int
:
#define INT_MAX +32767
Сводка об усеченной части
- Обычно
ptrdiff_t
не больше signed long
, что не меньше 32 бит. int
определяется как длина не менее 16 бит. - Следовательно, вычитание двух указателей может дать результат, который не вписывается в
int
вашей платформы. - Мы помним сверху, что для подписанных типов результат, который не соответствует, приводит к неопределенному поведению.
strlen30
применяется побитово или к указателю-вычитанию-результата:
| 32 bit |
ptr_diff |10111101111110011110111110011111| // could be even larger
& |00111111111111111111111111111111| // == 3FFFFFFF<sub>16</sub>
----------------------------------
= |00111101111110011110111110011111| // truncated
Предотвращает нестабильное поведение путем усечения результата вычитания указателя до максимального значения 3FFFFFFF 16 = 1073741823 10 .
Я не уверен, почему они выбрали именно это значение, потому что на большинстве машин только самый старший бит сообщает о подписи .Возможно, по сравнению со стандартом имел бы смысл выбрать минимум INT_MAX
, но 1073741823 действительно немного странно, не зная больше деталей (хотя, конечно, он прекрасно выполняет то, что говорится в комментарии над их функцией: обрезать до 30 бит и предотвратить переполнение).1109 *
"Почему бы не использовать strlen () для этой части"
и переписать его так:
return 0x3fffffff & (int)(strlen(z));
Я предполагаю, что они хотеличтобы избежать потенциальной косвенности.Другим преимуществом может быть меньшее количество зависимостей от стандартной библиотеки, что может быть полезно, если вы пишете не размещенное приложение.
Кстати, как следует из приведенных выше ссылок, (int)(strlen(z))
может привести к неопределенному поведению, если максимум для ptrdiff_t> INT_MAX
, поэтому (int)(0x3fffffff & strlen(z))
будет лучше.