Я уверен, что 99% из тех, кто программирует на С++, сталкивался с проблемой, когда имеется статический массив значений, количество элементов которого нужно передать вызываемой функции, присвоить другой переменной или использовать его ещё каким-либо способом. Самое первое, что приходит на ум - это разделить длину массива в байтах на размер типа элемента. Например:
static const int ARRAY[] = { 1, 2, 3, 4, 5, 6 };
static const size_t ELEMENTS_COUNT = sizeof(ARRAY) / sizeof(int);
DoSomethingUseful(ELEMENTS_COUNT);
Конечно, это работает, и работает правильно до тех пор, пока вы не решите изменить тип элементов массива. Хорошим тоном считается определение констант и переменных непосредственно перед их использованием, но представьте себе следующую ситуацию:
static const int ARRAY[] = { 1, 2, 3, 4, 5, 6 };
// 20 тысяч строк полезного кода
static const size_t ELEMENTS_COUNT = sizeof(ARRAY) / sizeof(int);
DoSomethingUseful(ELEMENTS_COUNT);
В этот момент ваш менеджер говорит, что код надо срочно адаптировать под платформу х64, используя все её преимущества, причём это должно быть сделано "вчера", и вы бросаетесь изменять тип int на __int64. В условиях острой нехватки времени я вам гарантирую, что хотя бы в одном участке кода вы забудете исправить sizeof(int) на sizeof(__int64). К чему это приведёт - рассказывать не стоит, особенно если ошибка окажется в редко используемой части кода. Как правило, крах вашей программы на глазах у заказчика заставит вас серьёзно и надолго задуматься.
Но не стоит отчаиваться, поскольку проблема решается просто - при вычислении размера массива вместо размера типа нужно использовать размер одного из элементов этого массива (обычно первого, поскольку в С++ разрешены массивы только ненулевой длины). Таким образом получаем следующее:
static const int ARRAY[] = { 1, 2, 3, 4, 5, 6 };
static const size_t ELEMENTS_COUNT = sizeof(ARRAY) / sizeof(ARRAY[0]);
Не знаю, как вам, но мне не нравится определение ELEMENTS_COUNT, поскольку оно засоряет пространство имён в своей области видимости, да и компилятор всё равно оптимизирует вычисление количества элементов, и в случае с вызовом DoSomethingUseful(ELEMENTS_COUNT) в скомпилированном с включенной оптимизацией бинарном коде это будет выглядеть примерно так:
push 6
call DoSomethingUseful
К тому же, писать постоянно такую конструкцию, как sizeof(ARRAY) / sizeof(ARRAY[0]), довольно утомительно, а если не использовать переменную ELEMENTS_COUNT, то вызов функции DoSomethingUseful будет довольно зловещим:
DoSomethingUseful(sizeof(ARRAY) / sizeof(ARRAY[0]));
Есть ли более элегантный способ вычисления количества элементов? Да, он существует, поскольку существует возможность передачи массива по ссылке в качестве параметра функции:
void F(int (&ARRAY)[6]);
Развивая мысль дальше, сделаем эту функцию шаблонной и в качестве параметров шаблона зададим тип элементов массива и количество его элементов. Ссылка на массив остаётся параметром функции, а возвращаемым значением является нужное нам число элементов:
template<typename T, size_t N>
inline size_t LengthOf(T (&x)[N])
{
return N;
}
Поскольку функция определена как встраиваемая (хотя компилятор в принципе может и проигнорировать это указание, но в этом случае всё получалось как и задумано), значение N будет вычислено на этапе компиляции. В результате получаем более краткий и читаемый код для вызова DoSomethingUseful:
DoSomethingUseful(LengthOf(ARRAY));
В скомпилированном коде по-прежнему без изменений:
push 6
call DoSomethingUseful
Таким образом, мы получили требуемый результат. Если кто-то знает более изящное решение - не стесняйтесь, напишите мне.
понедельник, 21 мая 2007 г.
Вычисление количества элементов статического массива на этапе компиляции
Автор: Ivan на 01:23
Подписаться на:
Комментарии к сообщению (Atom)
Комментариев нет:
Отправить комментарий