Ограничить доступ к массивам по неправильному индексу
Перефразировал вопрос, чтобы быть более понятным:
Я заинтересован в добавлении правила к контролерам Coverity и хочу проконсультироваться, если это возможно и что нужно сделать, чтобы достичь. Я говорю о программировании на C и хочу ограничить доступ к массивам с помощью определенного перечислителя, а не какого-либо индекса int.
Например, у меня есть 2 массива, апельсины и яблоки размером 5 и 10 клеток соответственно.
Чтобы избежать неправильного использования массивов, я хочу определить 2 перечисления (или typedefs, если необходимо), одно для апельсинов и одно для яблок.
Enum apples {
A0 = 0,
A1 = 1,
A2 = 2,
A3 = 3,
A4 = 4,
}
Enum oranges {
O0 = 0,
O1 = 1,
O2 = 2,
O3 = 3,
O4 = 4,
O5 = 5,
O6 = 6,
O7 = 7,
O8 = 8,
O9 = 9,
}
И я хочу добавить правило для покрытия, которое проверяет каждый доступ к этим массивам. Например:
Apples[A4]; //success
Apples[O0]; // coverity error
Apples[2]; // coverity error
Можно ли добавить такое правило?
3 ответа
Вы можете сделать функцию проверки, если она знает длину. Я не вижу другого решения, потому что C не проверяет индекс.
<array_type> accessArray(<array_type> array, int index, int len)
{
if (index > len || index < 0) {
return <error_value>; // return an error value set by you
} else {
return array[index];
}
}
Чтобы сделать что-то подобное в чистом C (вместо использования внешнего инструмента), вы можете использовать структуры вместо перечислений для представления допустимого диапазона индекса. Структуры строго типизированы, поэтому система типов не позволит вам использовать индексатор, предназначенный для одного массива, для доступа к другому.
Есть несколько способов сделать это: вы можете поместить индекс int внутри структуры или (при условии, что диапазон элементов является непрерывным), вы можете сделать вашу безопасность еще более жесткой, используя непосредственные идентификаторы структуры, а не любые открытые целые числа вообще.:
typedef struct { int UNUSED; } appleI;
typedef struct { int UNUSED; } orangeI;
const appleI apples[5] = { };
const orangeI oranges[10] = { };
apple_t * Apples(appleI * i) { //The actual array is hidden from view
static apple_t _Apples[] = { ... };
return &_Apples[i - &apples[0]];
}
orange_t * Oranges(orangeI * i) {
static orange_t _Oranges[] = { ... };
return &_Oranges[i - &oranges[0]];
}
#define A0 (&apples[0])
#define A1 (&apples[1])
// ...etc
...
orange_t myOrange = ...;
*Oranges(O2) = myOrange;
apple_t myApple = *Apples(A3);
потенциально вы можете добавить проверку диапазона в середину функций "массива", чтобы гарантировать, что новые экземпляры типов индексов не используются (что, безусловно, будет ошибкой - эти типы должны быть непрозрачными для пользовательского кода)
вы все еще можете использовать
A0 + 1
и так далеедля мощного компилятора все это должно быть довольно легко встроено и сведено к нулю
// ИСПОЛЬЗУЕМ МОДУЛЬНЫЙ ОПЕРАТОР, ЧТОБЫ ИЗБЕЖАТЬ ИЗ ГРАНИЦ
#include <stdio.h>
#define LIMIT 5
int main()
{
char array[LIMIT] = {'A', 'B', 'C', 'D', 'E'};
for(int i = 0; i < LIMIT + 3; i++)
printf("ELEMENT = '%c'\n", array[i%LIMIT]);
return 0;
}