Is enum a suitable way to implement a "local define?"

Discussion in 'C Programming' started by partremmaps, Apr 5, 2014.

  1. partremmaps

    partremmaps Guest

    Of course I could use malloc and all that but this isn't about that.

    Often I do like the following:

    int i
    #define sizeMAX 1024
    static char buffer[sizeMAX];
    i=0;
    while(i<sizeMAX)
    {
    putc(buffer);
    }
    .... lots more code that reference buffer[] and sizeMAX ...
    #undef sizeMAX


    But this seems messy because I could forget the #undef and then that #define is left active for anything following in the code.

    I tried const int sizeMAX=1024 but of course the declaration of buffer doesn't allow that as an array size specifier.

    However, doing "enum { sizeMAX=1024 };" seems to work, but I can't tell from a quick word search in the standard whether it is supposed to always work.

    Obviously enum was meant to map multiple words to a list of ints, and if the standard allows that list to be set up at run time then it may not work in some standard implementations.

    Logically, "const int x=5" would be of the same functionality as "enum {x=5}" however GCC treats them differently, allowing the enum'ed x to be used as the array size specifier while not allowing the const int to be used as such.

    Thanks,

    Jesse
     
    partremmaps, Apr 5, 2014
    #1
    1. Advertisements

  2. partremmaps

    Stefan Ram Guest

    I think C11 allows this, see: N1570 6.7.6.2 Array declarators.
    Since you do not seem to refer to the uncancelled and
    unreplaced standard, you might refere to some of the
    standards that now are cancelled and replaced. So which
    standard do you refer to with »the standard«?

    The size of an array that has not a variable lenght array
    type needs to be given as an integer constant expression.
    An enumeration constant is an integer constant expression
    while a variable of a const-qualified type is not.
     
    Stefan Ram, Apr 5, 2014
    #2
    1. Advertisements

  3. partremmaps

    Stefan Ram Guest

    I have failed to take into account that your array is static!

    In this case, I believe, it cannot be a variable-length array.

    So, an enum actually should be fine.
     
    Stefan Ram, Apr 5, 2014
    #3
  4. C90 does not have variable-length arrays.

    C99 does, but permits them only with automatic storage duration;
    you can't define a VLA at file scope or with the "static" keyword.

    C11 made VLA support, along with several other language features,
    optional. I'm skeptical that any implementers of C11 compilers will
    take advantage of that permission, but we'll see how it plays out.
     
    Keith Thompson, Apr 5, 2014
    #4


  5. It's not just GCC; that behavior is specified by the C standard. Given
    `const int x = 5;`, the expression `x` is not a constant expression, and
    cannot be used in any context that requires one.

    If you use a macro, the convention is to write the name in all-caps:

    #define SIZEMAX 1024
    static char buffer[SIZEMAX];

    This serves as a reminder to the reader that SIZEMAX is a macro, which
    is useful since macro names behave quite differently from other
    identifiers.

    Most programmers don't bother to remove the macro using #undef. If you
    choose the name carefully, there's usually no real need to do so.

    In this case, without the "#undef", both "SIZEMAX" and "buffer" are
    visible from the point where they're defined to the end of the
    translation unit, which seems like the behavior you'd want.

    As for using an "enum", that's perfectly valid as well. As you point
    out, enums aren't really intended for this kind of thing, but the
    language rules defining them permit this usage, and they aren't going to
    change.

    This declaration:

    enum { sizeMAX = 1024 };

    defines two things: an anonymous enumeration type (which is compatible
    with some implementation-defined integer type), and a constant of type
    int called sizeMAX. You can freely use `sizeMAX` as a constant
    expression; it's equivalent to the expression `1024`.

    One drawback is that you can't use this mechanism to define a constant
    of a type other than int, but that's probably not a problem in this
    case.

    It's admittedly odd that enumeration constants are of type int rather
    than of the enumerated type. It's for historical reasons.

    (C++ has different rules, BTW, but that needn't concern you unless your
    code is intended to be used both as C and as C++.)

    Incidentally, when posting to Usenet, it's helpful to use hard line
    breaks to keep your text down to less than 80 columns, preferably 72 or
    so. Google Groups layers a web interface on top of the much older
    Usenet text interface, and does a very poor job of it.
     
    Keith Thompson, Apr 5, 2014
    #5

  6. Also if you do
    #define SIZE_MAX 1024
    ....
    #undef SIZE_MAX
    ....
    #define SIZE_MAC 1000
    ....

    You're creating a nightmare for any maintaing programmer.

    In C, the natural unit is the source file. #defines should be treated as having
    file scope, or being exported.
    So either everything uses the same SIZE_MAX, or hardocde it. Beginners are
    often taught that magic constants are bad in code. There's some truth in that,
    but dependencies are worse. And if you're declaring a buffer, you need to
    understand that the function has a limit on the size of input it can handle,
    it doesn't vanish by putting a layer of in between the constant and the source.

    If 1024 becomes a problem, you'll need to go through the whole function very
    carefully to see what restrictions on N you have anyway, you might need to
    move to malloc() or put in assertion checks. It's rarely a case of editing
    the value without really looking at the code.
     
    Malcolm McLean, Apr 5, 2014
    #6
  7. partremmaps

    BartC Guest

    It is strange but C doesn't have what I call /named constants/, which are
    names applied to compile-time expressions.

    The nearest you will get is with the enum{} feature as you've used. Those
    names have the scope rules you expect, but it will only work for int types,
    which is not a problem for your intended use. It just seems odd to have to
    define:

    enum {sizeMAX = 1024};

    instead of, for example:

    constant sizeMAX = 1024;

    (Syntax such as 'const int' is a source of endless confusion for people not
    that familiar with C. The 'const' there means readonly. But I avoid it to
    reduce clutter and even more compiler type errors than I usually get; and in
    a complex type spec I can never figure out which part the const applies to
    anyway.)
     
    BartC, Apr 5, 2014
    #7
  8. partremmaps

    Ike Naar Guest



    (don't forget to increment i in the loop body!)
    The enum method will work.

    Sometimes it might be an option to forget about named constants,
    and use sizeof:

    static char buffer[1024];
    for (int i = 0; i < sizeof buffer / sizeof buffer[0]; ++i)
    {
    do_something(buffer);
    }
    /* ... */
    static double otherbuffer[33];
    for (int i = 0; i < sizeof otherbuffer / sizeof otherbuffer[0]; ++i)
    {
    do_something_else(otherbuffer);
    }

    or, using a #define to capture the sizeof expression:

    #define NUMBER_OF_ELEMENTS(array) (sizeof (array) / sizeof (array[0]))

    static char buffer[1024];
    for (int i = 0; i < NUMBER_OF_ELEMENTS(buffer); ++i)
    {
    do_something(buffer);
    }
    /* ... */
    static double otherbuffer[33];
    for (int i = 0; i < NUMBER_OF_ELEMENTS(otherbuffer); ++i)
    {
    do_something_else(otherbuffer);
    }
     
    Ike Naar, Apr 6, 2014
    #8
  9. partremmaps

    jacob navia Guest

    Le 05/04/2014 22:07, a écrit :
    The lcc-win compiler will accept that if you declare sizeMax as static.
     
    jacob navia, Apr 6, 2014
    #9
  10. partremmaps

    David Brown Guest

    Can't you get an unsigned int constant with

    enum { sizeMax = 1024u };

    and long and unsigned longs using "l" and "ul" suffixes?
     
    David Brown, Apr 6, 2014
    #10
  11. partremmaps

    David Brown Guest

    It is strange but C doesn't have what I call /named constants/, which are
    names applied to compile-time expressions.

    The nearest you will get is with the enum{} feature as you've used. Those
    names have the scope rules you expect, but it will only work for int types,
    which is not a problem for your intended use. It just seems odd to have to
    define:

    enum {sizeMAX = 1024};

    instead of, for example:

    constant sizeMAX = 1024;

    (Syntax such as 'const int' is a source of endless confusion for people not
    that familiar with C. The 'const' there means readonly. But I avoid it to
    reduce clutter and even more compiler type errors than I usually get;
    and in
    a complex type spec I can never figure out which part the const applies to
    anyway.)
    [/QUOTE]

    There is an easy solution to confusion about complex type specs - use
    typedef's to split it up so that every type is completely obvious. Step
    by step, no more than two or three parts per type, and you can't go
    wrong. And just as importantly, anyone having even a quick glance at
    your code can't go wrong either.

    For the most part, "static const int sizeMax = 1024;" does the same job
    as "enum { sizeMax = 1024 };" or "#define sizeMax 1024", but it does so
    with safer typing, better static error checking, more flexibility and -
    IMHO - clearer code. The only exception is that you can't use the
    "static const" for the size of statically allocated arrays. It's a
    painful omission from the standards.

    Of course, it is possible to write things in reverse:

    static char buffer[1024];
    static const int sizeMax = sizeof(buffer) / sizeof(buffer[0]);

    Whether or not you think that is better is a matter of opinion, but it's
    an alternative that avoids the #define or the somewhat unnatural enum,
    and still avoids using the "magic number" twice in the code.
     
    David Brown, Apr 6, 2014
    #11
  12. No. 1024u is of type unsigned int, but sizeMax is still of type int.

    For example, this:

    enum { too_big = UINT_MAX };

    is invalid (unless UINT_MAX == INT_MAX, which is possible but odd).

    N1570 6.7.2.2p2:

    Constraints

    The expression that defines the value of an enumeration constant
    shall be an integer constant expression that has a value
    representable as an int.

    The expression doesn't have to be of type int, but it has to be within
    the range of int.
     
    Keith Thompson, Apr 6, 2014
    #12
  13. partremmaps

    James Kuyper Guest

    "An identifier declared as an enumeration constant has type int."
    (6.4.4.3p2).

    "The expression that defines the value of an enumeration constant shall
    be an integer constant expression that has a value representable as an
    int." (6.7.2.2p2)
     
    James Kuyper, Apr 6, 2014
    #13
  14. partremmaps

    Ian Collins Guest

    Or if you want the constant without the buffer:

    typedef char Buffer[1024];
    const size_t sizeMax = sizeof(Buffer);
     
    Ian Collins, Apr 6, 2014
    #14
  15. partremmaps

    Ian Collins Guest

    Are you thinking of the improved enums in C++ here?
     
    Ian Collins, Apr 6, 2014
    #15
  16. partremmaps

    David Brown Guest

    That could be it - would sizeMax be an unsigned int in C++? (I believe
    that in C++, enums can be of different sizes, but I have not had
    occasion or need to learn the rules.)
     
    David Brown, Apr 6, 2014
    #16
  17. partremmaps

    Ian Collins Guest

    In C++ you could write

    enum : unsigned { sizeMax = 1024u };
     
    Ian Collins, Apr 6, 2014
    #17
  18. partremmaps

    BartC Guest

    Sorry, but anything that involves doing X, Y or Z to make type specs 'easy'
    isn't a solution! It shouldn't be necessary to do anything; they should be
    self-explanatory. However if you are stuck with writing actual C (I'm not)
    then I suppose you might need to use these methods.

    (But I would probably still never use 'const' anyway.)
    But, sizeMax is still, as far as I can gather, some value with reserved
    storage, that you can take the address of. And you can't declare another
    array using the same size as you say.

    So it still falls short of what you can do with a proper named constant.

    You're right that it's another thing that would have been very easy to get
    right, but forty years on and the language still doesn't have a plain,
    straightforward way of declaring a named constant!

    (It's a little crazy really; I have now several language projects where I
    translate source code that has 'proper' named constants, into actual C. Did
    I use #defines, or enums? Both have limitations to do with scope and type.
    But I've just checked and the solution I came up with was the following; if
    the source uses this (made C-like so as not to frighten anyone):

    const sizeMax = 1024 /* 'int' is optional */
    char buffer[sizeMax]
    int i=sizeMax

    The generated C is just this:

    char buffer[1024];
    int i=1024;

    the problem simply disappears! It's almost a non-issue, unless you have
    to write actual C.)
     
    BartC, Apr 6, 2014
    #18
  19. partremmaps

    James Kuyper Guest

    On 04/06/2014 05:30 PM, David Brown wrote:
    ....
    The sizes of enumerated types can vary in C, too; it's only enumeration
    constants that are of type 'int'. That means that there's not much point
    is making an enumerated type be bigger than an int, but it could
    usefully be smaller.
     
    James Kuyper, Apr 6, 2014
    #19
  20. That's not the only exception. Given the "static const int" declaration
    sizeMax is not a constant expression, and there are a number of contexts
    in which it can't be used (in a case label, for example).

    I don't know that I'd call it a "painful omission", but I agree that it
    would be nice to have a way to define name constants. Copying C++'s
    semantics would probably be a decent approach.
    One problem with that is that to doesn't give a name to the value 1024.
    For example, you might want two or more buffers with the same size.
     
    Keith Thompson, Apr 7, 2014
    #20
    1. Advertisements

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.