|
Here is a different take on it. We can use #define to inform the header about the properties of certain symbols. Here is my oob.c program. I will show the output, and then the content of "oob.h". #include <stdlib.h>
#include <stdio.h>
#include "oob.h"
int oob_fail(const char *file, int line)
{
fprintf(stderr, "%s:%d:out of bounds array access\n", file, line);
abort();
}
/*
* Declare properties of array type x
*/
#define ARRAY_ELTYPE_x int /* element type is int */
#define ARRAY_SIZE_x 7 /* number of elements is 7 */
/*
* Ensure array type x is fully declared at file scope
*/
ARRAY_FULLTYPE(x);
/*
* Inform the OOB module that the identifiers p and a are
* used as variables related to type x: either pointers
* to it or values.
*/
#define ARRAY_TYPEOF_p x
#define ARRAY_TYPEOF_a x
int get_elem(ARRAY_TYPE(x) *p, int i)
{
return APREF(p, i);
}
int main(void)
{
ARRAY_TYPE(x) a = ARRAY_INIT(1, 2, 3);
for (size_t i = 0; i <= ARRAY_SIZEOF(a); i++)
printf("a[%zd] == %d\n", i, get_elem(&a, i));
return 0;
}
Output: $ ./oob
a[0] == 1
a[1] == 2
a[2] == 3
a[3] == 0
a[4] == 0
a[5] == 0
a[6] == 0
oob.c:31:out of bounds array access
Aborted (core dumped)
The content of "oob.h" #ifndef OOB_H_435E_FDE9
#define OOB_H_435E_FDE9
int oob_fail(const char *file, int line);
#define OOB_PREFIX oob_ident_
#define OOB_XCAT(X, Y) X ## Y
#define OOB_CAT(X, Y) OOB_XCAT(X, Y)
#define ARRAY_ELTYPE(T) OOB_CAT(ARRAY_ELTYPE_, T)
#define ARRAY_SIZE(T) OOB_CAT(ARRAY_SIZE_, T)
#define ARRAY_TAG(T) OOB_CAT(ARRAY_TAG_, T)
#define ARRAY_FULLTYPE(T) \
struct ARRAY_TAG(T) { \
ARRAY_ELTYPE(T) a[ARRAY_SIZE(T)]; \
}
#define ARRAY_TYPE(T) struct ARRAY_TAG(T)
#define ARRAY_TYPEOF(V) OOB_CAT(ARRAY_TYPEOF_, V)
#define ARRAY_SIZEOF(V) ARRAY_SIZE(ARRAY_TYPEOF(V))
#define ARRAY_INIT(...) { { __VA_ARGS__ } }
#define AREF(ARRAY, I) \
(((size_t) (I) >= ARRAY_SIZEOF(ARRAY)) \
? oob_fail(__FILE__, __LINE__), (ARRAY).a[0] \
: (ARRAY).a[I])
#define APREF(PARRAY, I) \
(((size_t) (I) >= ARRAY_SIZEOF(PARRAY)) \
? oob_fail(__FILE__, __LINE__), (PARRAY)->a[0] \
: (PARRAY)->a[I])
#endif
Preprocessor invoked on oob.c (snipped down to the relevant part after the run-time support function oob_fail): struct ARRAY_TAG_x { int a[7]; };
int get_elem(struct ARRAY_TAG_x *p, int i)
{
return (((size_t) (i) >= 7) ? oob_fail("oob.c", 31), (p)->a[0] : (p)->a[i]);
}
int main(void)
{
struct ARRAY_TAG_x a = { { 1, 2, 3 } };
for (size_t i = 0; i <= 7; i++)
printf("a[%zd] == %d\n", i, get_elem(&a, i));
return 0;
}
It's clean enough to be readable (except, of course, code dense with AREF or APREF calls will be a mess). Uses arrays wrapped in structs, so you can pass arrays by value.You have to make a list of your variables that are involved and write some #define lines for them. Same for the array types. |