Macro argument bounds checking?

Discussion in 'C Programming' started by Jim Cook, Sep 17, 2003.

  1. Jim Cook

    Jim Cook Guest

    We have a macro which takes various index constants as an argument and
    offsets into an array. The macro can be an Lvalue or Rvalue. The index
    is not zero based. I would like a compile time error displayed if the
    index is out of bounds. Is there a way to do this well?

    I read through the FAQ that I could find mentioned, and did not see this
    sort of question in the preprocessor section anywhere.

    What I came up with is below. It does in fact generate errors using my
    16-bit and 32-bit compilers, but it feels like I'm relying a little bit
    on the behavior of the MS compiler and would like to know a portable
    proper C method.

    Thank you for any time spent on this.


    The code and run-time clip are below. The valid index range is from 20
    to 27 inclusive. Any other value should produce an error. Testing for
    non-integer is not required.


    // #define argument range testing
    #include "limits.h"

    volatile unsigned short usArray[8];
    volatile unsigned short usI;
    volatile unsigned char ucArray[8];
    volatile unsigned char ucI;

    #define usAccess(r) \
    (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
    #define ucAccess(r) \
    (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])

    void main(void)
    {
    usAccess(19) = 19; /* bad */
    usAccess(20) = 20;
    usAccess(27) = 27;
    usAccess(28) = 28; /* bad */
    usI = usAccess(19); /* bad */
    usI = usAccess(20);
    usI = usAccess(27);
    usI = usAccess(28); /* bad */
    ucAccess(19) = 19; /* bad */
    ucAccess(20) = 20;
    ucAccess(27) = 27;
    ucAccess(28) = 28; /* bad */
    ucI = ucAccess(19); /* bad */
    ucI = ucAccess(20);
    ucI = ucAccess(27);
    ucI = ucAccess(28); /* bad */
    }
    #if 0
    ------------------ Output ------------------
    C:\TEMP\>cl /c /W3 test.c
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
    Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

    test.c
    test.c(16) : warning C4307: '*' : integral constant overflow
    test.c(19) : warning C4307: '*' : integral constant overflow
    test.c(20) : warning C4307: '*' : integral constant overflow
    test.c(23) : warning C4307: '*' : integral constant overflow
    test.c(24) : warning C4307: '*' : integral constant overflow
    test.c(27) : warning C4307: '*' : integral constant overflow
    test.c(28) : warning C4307: '*' : integral constant overflow
    test.c(31) : warning C4307: '*' : integral constant overflow
    #endif
    Jim Cook, Sep 17, 2003
    #1
    1. Advertising

  2. "Jim Cook" <> wrote in message
    news:bk880s$...
    | We have a macro which takes various index constants as an argument and
    | offsets into an array. The macro can be an Lvalue or Rvalue. The index
    | is not zero based. I would like a compile time error displayed if the
    | index is out of bounds. Is there a way to do this well?
    ....
    | volatile unsigned short usArray[8];
    | volatile unsigned short usI;
    ....
    | #define usAccess(r) \
    | (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])

    Here is something that will fail more reliably, and should
    work I think (though it's at the limits of the standard):

    volatile unsigned short usArray[8];

    #define usAccess(r) \
    ( *((unsigned short(*)[20<=r&&r<=27])usArray)[r-20] )

    int main()
    {
    usAccess(20) = 20;
    usAccess(21) = 21;
    usAccess(27) = 27;
    usAccess(19) = 19; /* bad */
    usAccess(28) = 28; /* bad */
    }

    The idea is that the conditional expression is used
    to define the size of an array ( 0 or 1 ).
    The following type expression defines a pointer
    to that array:
    (unsigned short(*)[20<=r&&r<=27])

    A pointer of that type is then used to perform offset
    calculations. And this will fail/be illegal if the array
    size is zero (some compilers may even complain before).

    A key feature of this approach is that it will fail
    to compile if the parameter of usAccess is not a compile-
    time constant (while the macro you posted would just
    lead to undefined behavior...).


    This is really ugly though...
    (I'd rather use some C++-based technique than this...)


    Regards,
    --
    http://ivan.vecerina.com
    Ivan Vecerina, Sep 17, 2003
    #2
    1. Advertising

  3. On Tue, 16 Sep 2003 23:57:16 UTC, Jim Cook <>
    wrote:

    > We have a macro which takes various index constants as an argument and
    > offsets into an array. The macro can be an Lvalue or Rvalue. The index
    > is not zero based. I would like a compile time error displayed if the
    > index is out of bounds. Is there a way to do this well?
    >
    > I read through the FAQ that I could find mentioned, and did not see this
    > sort of question in the preprocessor section anywhere.
    >
    > What I came up with is below. It does in fact generate errors using my
    > 16-bit and 32-bit compilers, but it feels like I'm relying a little bit
    > on the behavior of the MS compiler and would like to know a portable
    > proper C method.
    >
    > Thank you for any time spent on this.
    >
    >
    > The code and run-time clip are below. The valid index range is from 20
    > to 27 inclusive. Any other value should produce an error. Testing for
    > non-integer is not required.
    >
    >
    > // #define argument range testing
    > #include "limits.h"
    >
    > volatile unsigned short usArray[8];
    > volatile unsigned short usI;
    > volatile unsigned char ucArray[8];
    > volatile unsigned char ucI;
    >
    > #define usAccess(r) \
    > (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
    > #define ucAccess(r) \
    > (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])


    Both macros are wrong!
    Please name a number that can be in r that is both, <= 20 AND >= 27 as
    required to get TRUE from the ?: operator.
    The numbers 21 trough 26 in r will fail on && because the left hand
    side of && will tell FALSE and the right hand side will never been
    tested.
    The number 20 (like all lower numbers) in r will deliver 0 because it
    is truly lower than 27.
    The numer 27 (like all numbers greater 20) will fail because it is >
    than 20.

    May be you means || but even than your limits are wrong.

    > ------------------ Output ------------------
    > C:\TEMP\>cl /c /W3 test.c
    > Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
    > Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
    >
    > test.c
    > test.c(16) : warning C4307: '*' : integral constant overflow
    > test.c(19) : warning C4307: '*' : integral constant overflow
    > test.c(20) : warning C4307: '*' : integral constant overflow
    > test.c(23) : warning C4307: '*' : integral constant overflow
    > test.c(24) : warning C4307: '*' : integral constant overflow
    > test.c(27) : warning C4307: '*' : integral constant overflow
    > test.c(28) : warning C4307: '*' : integral constant overflow
    > test.c(31) : warning C4307: '*' : integral constant overflow
    > #endif
    >

    2*INT_MAX is integral constant overflow!

    --
    Tschau/Bye
    Herbert

    eComStation 1.1 Deutsch Beta ist ver├╝gbar
    The Real OS/2 Guy, Sep 17, 2003
    #3
  4. "The Real OS/2 Guy" <> wrote:

    >On Tue, 16 Sep 2003 23:57:16 UTC, Jim Cook <>
    >wrote:
    >
    >> We have a macro which takes various index constants as an argument and
    >> offsets into an array. The macro can be an Lvalue or Rvalue. The index
    >> is not zero based. I would like a compile time error displayed if the
    >> index is out of bounds. Is there a way to do this well?
    >>
    >> I read through the FAQ that I could find mentioned, and did not see this
    >> sort of question in the preprocessor section anywhere.
    >>
    >> What I came up with is below. It does in fact generate errors using my
    >> 16-bit and 32-bit compilers, but it feels like I'm relying a little bit
    >> on the behavior of the MS compiler and would like to know a portable
    >> proper C method.
    >>
    >> Thank you for any time spent on this.
    >>
    >>
    >> The code and run-time clip are below. The valid index range is from 20
    >> to 27 inclusive. Any other value should produce an error. Testing for
    >> non-integer is not required.
    >>
    >>
    >> // #define argument range testing
    >> #include "limits.h"
    >>
    >> volatile unsigned short usArray[8];
    >> volatile unsigned short usI;
    >> volatile unsigned char ucArray[8];
    >> volatile unsigned char ucI;
    >>
    >> #define usAccess(r) \
    >> (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])

    ^^^^^
    >> #define ucAccess(r) \
    >> (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])

    ^^^^^
    >
    >Both macros are wrong!
    >Please name a number that can be in r that is both, <= 20 AND >= 27 as

    ^^^^^^
    Once again, you failed to read carefully.

    >required to get TRUE from the ?: operator.
    >The numbers 21 trough 26 in r will fail on && because the left hand
    >side of && will tell FALSE and the right hand side will never been
    >tested.

    This is plain wrong.

    20 <= 20 && 20 <= 27 /* TRUE */
    20 <= 21 && 21 <= 27 /* TRUE */
    20 <= 22 && 22 <= 27 /* TRUE */
    20 <= 23 && 23 <= 27 /* TRUE */
    20 <= 24 && 24 <= 27 /* TRUE */
    20 <= 25 && 25 <= 27 /* TRUE */
    20 <= 26 && 26 <= 27 /* TRUE */
    20 <= 27 && 27 <= 27 /* TRUE */

    Quod erat demonstrandum.

    <SNIP>

    Irrwahn
    --
    6 * 9 = 42 (base 13)
    Irrwahn Grausewitz, Sep 17, 2003
    #4
  5. Jim Cook

    Kevin Easton Guest

    Jim Cook <> wrote:
    > We have a macro which takes various index constants as an argument and
    > offsets into an array. The macro can be an Lvalue or Rvalue. The index
    > is not zero based. I would like a compile time error displayed if the
    > index is out of bounds. Is there a way to do this well?
    >
    > I read through the FAQ that I could find mentioned, and did not see this
    > sort of question in the preprocessor section anywhere.
    >
    > What I came up with is below. It does in fact generate errors using my
    > 16-bit and 32-bit compilers, but it feels like I'm relying a little bit
    > on the behavior of the MS compiler and would like to know a portable
    > proper C method.
    >
    > Thank you for any time spent on this.
    >
    >
    > The code and run-time clip are below. The valid index range is from 20
    > to 27 inclusive. Any other value should produce an error. Testing for
    > non-integer is not required.
    >
    >
    > // #define argument range testing
    > #include "limits.h"
    >
    > volatile unsigned short usArray[8];
    > volatile unsigned short usI;
    > volatile unsigned char ucArray[8];
    > volatile unsigned char ucI;
    >
    > #define usAccess(r) \
    > (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
    > #define ucAccess(r) \
    > (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])


    Try:

    #define usAccess(n) \
    (*((void)(sizeof(int[((n) < 0 || (n) > 10) ? -1 : 1])), &usArray[n]))

    The idea being to force a compile-time error by attempting to take the
    sizeof an array with negative size. (I can't recall if conditional
    operators are prohibited in compile-time constants - if so, just
    multiple the result of the || operator by -2 and add 1). Methods based
    on arrays of zero size won't error on gcc in non-pedantic mode, and
    possibly other compilers.

    - Kevin.
    Kevin Easton, Sep 17, 2003
    #5
  6. Jim Cook

    Capstar Guest

    The Real OS/2 Guy wrote:
    >>
    >>#define usAccess(r) \
    >> (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
    >>#define ucAccess(r) \
    >> (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])

    >
    >
    > Both macros are wrong!
    > Please name a number that can be in r that is both, <= 20 AND >= 27 as
    > required to get TRUE from the ?: operator.


    But that's not what the macro states. It states:

    (20<=r && r<=27 ? 0 : INT_MAX)

    which is the same as:

    (r>=20 && r<=27 ? 0 : INT_MAX)

    So in my opinion that means r must be bigger or equal than 20 AND
    smaller or equal than 27, which is not impossible if you ask me.

    Mark
    Capstar, Sep 17, 2003
    #6
  7. Jim Cook

    Jim Cook Guest

    > Here is something that will fail more reliably, and should
    > work I think (though it's at the limits of the standard):
    >
    > volatile unsigned short usArray[8];
    >
    > #define usAccess(r) \
    > ( *((unsigned short(*)[20<=r&&r<=27])usArray)[r-20] )


    Thank you. This is not only a perfect solution, but teaches me something
    as well. I see also that Kevin gave essentially the same answer.

    I would also like to thank Irrwahn and Mark for replying to the person
    who did not read the question very carefully.
    Jim Cook, Sep 17, 2003
    #7
  8. On Wed, 17 Sep 2003 11:41:32 +0200, "Ivan Vecerina"
    <NONE_use_form@website_to_contact_me> wrote:

    > "Jim Cook" <> wrote in message
    > news:bk880s$...
    > | We have a macro which takes various index constants as an argument and
    > | offsets into an array. The macro can be an Lvalue or Rvalue. The index
    > | is not zero based. I would like a compile time error displayed if the
    > | index is out of bounds. Is there a way to do this well?
    > ...
    > | volatile unsigned short usArray[8];
    > | volatile unsigned short usI;
    > ...
    > | #define usAccess(r) \
    > | (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
    >
    > Here is something that will fail more reliably, and should
    > work I think (though it's at the limits of the standard):
    >
    > volatile unsigned short usArray[8];
    >
    > #define usAccess(r) \
    > ( *((unsigned short(*)[20<=r&&r<=27])usArray)[r-20] )

    <snip>
    > The idea is that the conditional expression is used
    > to define the size of an array ( 0 or 1 ).
    > The following type expression defines a pointer
    > to that array:
    > (unsigned short(*)[20<=r&&r<=27])
    >
    > A pointer of that type is then used to perform offset
    > calculations. And this will fail/be illegal if the array
    > size is zero (some compilers may even complain before).
    >

    It is a constraint violation (if constant, see below) and must be
    diagnosed in a conforming compiler, but it need not fail to compile.
    At least gcc supports zero-bound arrays as an extension, and by
    default (if you don't ask for -pedantic[-errors]) silently. Using -1
    instead of 0, as Kevin Easton does elsethread, will fix that part; it
    is possible (though IMO not that useful) to define consistent
    semantics for zero bound, but not for negative bound.

    Also, for in-range values this effectively changes (reduces) the type
    of the lvalue used to unsigned short [1], so any access other than to
    (20) is arguably technically illegal -- is the "array object" in 6.5.6
    the designated one or the underlying one? -- plus the nominal
    dereference violates 6.5.p7. It is exceedingly (vanishingly?) rare
    for a C implementation to enforce these as long as the actual access
    to the actual object is valid, and in fact usually as long as the
    actual access is just to usable memory; but it legally could. To be
    absolutely safe, and also clear, I would use [valid? actualbound: -1].
    And yes, ?: is permitted in constant expressions; assignments are not,
    and fetches (thus, any objects) are not in most cases; neither is
    comma operator, perhaps on grounds that it was envisioned (only) to
    allow side-effects like assignment, but that is an easy extension.

    Plus an implementation "may accept other forms of constant
    expressions" (6.6p10) although it would be pretty perverse to accept
    things that are not in fact compile-time constant; I suspect this was
    intended to allow things like sin(), and maybe (pure) functions for
    which a definition is available and can be evaluated at compile time,
    even though general function calls are prohibited. It could also be
    used in C90 to allow not-really-constant aggregate local initializers;
    C99 allows this directly.

    > A key feature of this approach is that it will fail
    > to compile if the parameter of usAccess is not a compile-
    > time constant (while the macro you posted would just
    > lead to undefined behavior...).
    >

    In C90 it must be diagnosed if not constant (see above). In C99 it
    (quietly!) becomes a VLA and if the bound evaluates to <= 0 it is
    AFAICT only UB (6.2.5p20) and need not be diagnosed.

    Bitfield sizes and enumerator explicit values must still be integer
    constant expressions, and the former must be nonnegative and with a
    name strictly positive but <= an implementation defined limit, in all
    cases as constraint violations which must be diagnosed. And even gcc,
    at least as of 2.95.2, hasn't extended these. Also subscripts in a
    designated initializer, but only in C99; and case labels and #[el]if
    expressions, but there's no (standard) way to use those in an
    expression and thus the desired macro.

    - David.Thompson1 at worldnet.att.net
    Dave Thompson, Sep 22, 2003
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Casey Hawthorne
    Replies:
    21
    Views:
    862
    Roedy Green
    Jun 5, 2004
  2. Chris

    Array bounds checking

    Chris, Jul 5, 2005, in forum: Java
    Replies:
    5
    Views:
    775
  3. Casey Hawthorne
    Replies:
    16
    Views:
    1,198
  4. Julian Zhang

    Bounds Checking?

    Julian Zhang, Jan 12, 2004, in forum: C Programming
    Replies:
    3
    Views:
    590
    E. Robert Tisdale
    Jan 20, 2004
  5. Dave Rahardja
    Replies:
    4
    Views:
    577
    Dave Rahardja
    Nov 21, 2005
Loading...

Share This Page