Ways to define C constants

J

James Harris

I'm a bit confused over how 'best' to define constants - especially small
integer constants - in C. There seem to be some options so I wondred if you
guys would recommend one over the others.

I'll use a 16-bit port number in each example. The ui16 in the last example
is the typedef of a 16-bit unsigned int.

First option, with #define.
/* Define address port */
#define PORT_A 0x70

Second option, using enum
enum {
port_a = 0x70 /* Address port */
};

Third option, using const
static const ui16 port_a = 0x0070; /* Address port */

Are they all valid and are there any other ways to define integer constants?

I think I know the basics (#define is preprocessor, textual, works for more
than just numbers, widely used, needs parens if an expression etc; enum is
good for assigning consecutive integers, includes limited type info,
possibly modifiable through a pointer(?); const not guaranteed to be safe as
can be modified through a pointer but includes good type info).

Assuming they are all valid, under what circumstances would you choose one
over the others? If you had to use #define for some constants such as string
literals would you use #define for the integer constants too for
consistency?

Would you ever define an enum tag and/or define a variable using the enum?

I know the static keyword would keep the id private to the source file but
would the const be better with or without static? Would that depend on
whether the definition was in the .c source file or was in a header file
that might be included in multiple object files?

Finally, what would you recommend wrt capitalisation of the identifier name?
As you can see, I've used both lower case and upper case in the different
examples. Aside from the #define I'm not sure what's considered to be more
normal.

Sorry that's a lot of questions!

James
 
M

Malcolm McLean

I'm a bit confused over how 'best' to define constants - especially small
integer constants - in C. There seem to be some options so I wondred if you
guys would recommend one over the others.


I'll use a 16-bit port number in each example. The ui16 in the last example
is the typedef of a 16-bit unsigned int.
The ideal method for something like a port is this.

int main()
{
/* use Ox74 for port A */
intitialiseIO(0x74, "An argument", "Another setting");
}

intitialiseIO(int porta, char *setting1, char *setting2)
{
/* set port a to 0x74, then pass it down to all other IO */
}

Since PORT_A is used in only one place, it hardly matters whether you
#define it or hardcode it with a comment.

For various reasons this isn't always possible, but it's good general
principle.
 
K

Kleuske

I'm a bit confused over how 'best' to define constants - especially
small integer constants - in C. There seem to be some options so I
wondred if you guys would recommend one over the others.

I'll use a 16-bit port number in each example. The ui16 in the last
example is the typedef of a 16-bit unsigned int.

First option, with #define.
/* Define address port */
#define PORT_A 0x70

Second option, using enum
enum {
port_a = 0x70 /* Address port */
};

Third option, using const
static const ui16 port_a = 0x0070; /* Address port */

Are they all valid and are there any other ways to define integer
constants?

The 'static'-bit in the last option is out of place. Apart from that,
the last option does not deliver a const expression, so you can't use it
in (for instance) array-sizes or case labels. The other options are
(almost) equivalent.

See question 2.22 and 11.8.
I think I know the basics (#define is preprocessor, textual, works for
more than just numbers, widely used, needs parens if an expression etc;
enum is good for assigning consecutive integers, includes limited type
info, possibly modifiable through a pointer(?); const not guaranteed to
be safe as can be modified through a pointer but includes good type
info).

Assuming they are all valid, under what circumstances would you choose
one over the others?

Depends on the situation.

If I want some constants (and I don't really care what exactly they are,
as long as they're different), I'd choose enums. If I want constants to
be set at compile time (e.g. to set configuration stuff) I'd go for

#ifndef SOMETHING
#define SOMETHING "something"
#endif
If you had to use #define for some constants such
as string literals would you use #define for the integer constants too
for consistency?

Probably. Again, the situation above (some constant number and I don't
really care what it is) would be an exception.
Would you ever define an enum tag and/or define a variable using the
enum?

I know the static keyword would keep the id private to the source file
but would the const be better with or without static?

You can export the constant, which may be a advantage. Still, you can't
use it as a case label or size-specifier for an array.
Would that depend
on whether the definition was in the .c source file or was in a header
file that might be included in multiple object files?

Finally, what would you recommend wrt capitalisation of the identifier
name?

Stick to the coding-style in vogue where you work and be consistent in
using it. Any number of arguments can (and have) been made for almost any
style you care to come up with. Arguing about it is usually not very
productive. It's (mostly) a question of taste and 'de gustibus non
disputandum est'.

As long as it's not a viable entry to the obfuscated-C-contest,that is.
As you can see, I've used both lower case and upper case in the
different examples. Aside from the #define I'm not sure what's
considered to be more normal.

Sorry that's a lot of questions!

Not to worry. That's why this group exists.
 
D

David Brown

I'm a bit confused over how 'best' to define constants - especially small
integer constants - in C. There seem to be some options so I wondred if you
guys would recommend one over the others.

I'll use a 16-bit port number in each example. The ui16 in the last example
is the typedef of a 16-bit unsigned int.

First option, with #define.
/* Define address port */
#define PORT_A 0x70

Second option, using enum
enum {
port_a = 0x70 /* Address port */
};

Third option, using const
static const ui16 port_a = 0x0070; /* Address port */

Are they all valid and are there any other ways to define integer constants?

I think I know the basics (#define is preprocessor, textual, works for more
than just numbers, widely used, needs parens if an expression etc; enum is
good for assigning consecutive integers, includes limited type info,
possibly modifiable through a pointer(?); const not guaranteed to be safe as
can be modified through a pointer but includes good type info).

Assuming they are all valid, under what circumstances would you choose one
over the others? If you had to use #define for some constants such as string
literals would you use #define for the integer constants too for
consistency?

Would you ever define an enum tag and/or define a variable using the enum?

I know the static keyword would keep the id private to the source file but
would the const be better with or without static? Would that depend on
whether the definition was in the .c source file or was in a header file
that might be included in multiple object files?

Finally, what would you recommend wrt capitalisation of the identifier name?
As you can see, I've used both lower case and upper case in the different
examples. Aside from the #define I'm not sure what's considered to be more
normal.

Sorry that's a lot of questions!

James

All three methods will work.

It is best to use "static const" rather than plain "const" in a case
like this. In general, use "static" for file-level objects whenever it
is possible. For a "static const int" definition like this, you can put
it in a header or a C file - as long as your compiler has optimisation
enabled, and you don't do anything odd like take its address, the
compiler will generate as optimal code as if you had used the #define
method, and you have the advantage of the type information.

You cannot /legally/ modify a const "variable" through a pointer - it
would be undefined behaviour. So the compiler can optimise with the
assumption that the value never changes - and no one knows what will
happen if you /do/ try to change it.

Regarding capitalisation, it is a common but not universal custom to
capitalise #define'd macros. (Personally, I rarely capitalise them -
but I believe I am in the minority there.) Opinions are much more
divided about enum constants and static const's. I don't capitalise
them - but some people do. /You/ have to decide if the capitalisation
improves the legibility of /your/ code.


Don't use made-up types such as "ui16" - prefer to use the standard C99
types from <stdint.h>, such as uint16_t. They are standardised and
commonly used, and let you write more portable code.


One other thing to consider here is that you are probably not very
interested in the address of port A. What you are usually interested in
is the contents of the register at that port. A common way to do that
is something like this:

#define portA (*((volatile uint8_t *) 0x0070))

Then you can write things like "portA |= 0x01;" to turn on bit 1 of port A.
 
B

BartC

int main()
{
/* use Ox74 for port A */
intitialiseIO(0x74, "An argument", "Another setting");
}

intitialiseIO(int porta, char *setting1, char *setting2)
{
/* set port a to 0x74, then pass it down to all other IO */
}

Since PORT_A is used in only one place, it hardly matters whether you
#define it or hardcode it with a comment.

For various reasons this isn't always possible, but it's good general
principle.

It's a terrible general principle!

Always name constants like this. Then you will know (if named properly) that
this is the port address, something that is not at all certain from the
above (since you talk about setting the port to 0x74).

Also if there are any other 0x74 values floating around, nothing to do with
ports, then they won't get mixed up. (And when a new version has the port on
0x84, you won't inadvertently change something entirely unrelated.)
 
B

BartC

All three methods will work.

Not really. The 'const' method won't deliver a value that can be used in
many situations where you want an actual compile-time expression. (Array
bounds and case-labels have been mentioned. They will apparently work for
arrays, but might quietly create a VLA instead. They also can't be used to
construct other compile-time expressions.)

The enum method only works for signed integers (otherwise is the best of the
bunch).

#define has all sorts of issues with scope (using it for a private constant
inside a function, for example, is a bit of a pain). Also any expression
used to define it, could be re-evaluated, with potentially different
results, at each point of use.)
Don't use made-up types such as "ui16" - prefer to use the standard C99
types from <stdint.h>, such as uint16_t. They are standardised and
commonly used, and let you write more portable code.

Disagree. So long as 'ui16' is itself defined in terms of uint16_t, then I
can't see the problem. It's not as though the name is completely obscure or
misleading.
 
M

Malcolm McLean

It's a terrible general principle!

Always name constants like this. Then you will know (if named properly) that
this is the port address, something that is not at all certain from the
above (since you talk about setting the port to 0x74).
The constant is used once. From then on, it's a variable. Since it only appears once,
it's not important whether it appears as a literal or not.
Also if there are any other 0x74 values floating around, nothing to do with
ports, then they won't get mixed up. (And when a new version has the port on
0x84, you won't inadvertently change something entirely unrelated.)
The snag with the #define method is that it's easy to write code which relies
on the #define being a certain value. Then when we change the definition, it
breaks. If the number is a variable, that's still possible, but it's much less likely.

Also, you can't use the same subroutine to write a byte to port 0x74 and 0x84,
everything is tied to having a single port.
 
J

James Kuyper

I'm a bit confused over how 'best' to define constants - especially small
integer constants - in C. There seem to be some options so I wondred if you
guys would recommend one over the others.

The term "integer constant" is defined by section 6.4.4.1. It refers
very specifically to things like 1, 2U, 3L, 04, and 0x5. None of the
things you give below is an integer constant.
I'll use a 16-bit port number in each example. The ui16 in the last example
is the typedef of a 16-bit unsigned int.

First option, with #define.
/* Define address port */
#define PORT_A 0x70

This defines a macro that expands into an integer constant. For
practical purposes, it's effectively the same as an integer constant,
except that preprocessor directives can test whether or not it has been
#defined.
Second option, using enum
enum {
port_a = 0x70 /* Address port */
};

With this definition, port_a is an enumeration constant, rather than an
integer constant. It does, however, qualify as an integer constant
expression.
Third option, using const
static const ui16 port_a = 0x0070; /* Address port */

With this definition, port_a is an expression of integer type with a
constant value. However, it does not qualify as either an integer
constant or as an integer constant expression:

"An _integer constant expression_ shall have integer type and shall only
have operands that are integer constants, enumeration constants,
character constants, sizeof expressions whose results are integer
constants, _Alignof expressions, and floating constants that are the
immediate operands of casts. Cast operators in an integer constant
expression shall only convert arithmetic types to integer types, except
as part of an operand to the sizeof or _Alignof operator." 6.6p6

As a result, the third version of port_a cannot be used for any of the
following purposes:
1. The dimension of an array with static storage duration.
2. The size of a bit-field.
3. The value of an enumeration constant.
4. The argument of an _Alignas() specifier.
5. The subscript of an array element designator in a designated initializer.
6. The controlling expression of a _static_assert().
7. The expression in a case: label.

I think it should be pretty easy to avoid using port_a in any of those
contexts.

In C++, because port_a is const and initialized with an integer constant
expression, port_a can qualify as an integral constant expression
itself, and can therefore be used in all of those locations. I know of
no reason why C couldn't adopt the same rule, and I think it would be a
good idea - but it hasn't been done yet.
... enum is
good for assigning consecutive integers, includes limited type info,
possibly modifiable through a pointer(?);

No, it is not modifiable by any means.
... const not guaranteed to be safe as
can be modified through a pointer but includes good type info).

There's no point in worrying about it being modified. The behavior of a
program that attempts to modify the value of an object that has been
declared const is undefined (6.7.3p6). One of the most likely
possibilities is the value will not change. Even if it does change, that
fact will be the least of your worries, if you execute such a program.
Assuming they are all valid, under what circumstances would you choose one
over the others? If you had to use #define for some constants such as string
literals would you use #define for the integer constants too for
consistency?

I normally use #define in virtually all contexts. In C++, I would use
the const approach, because it has better type safety, and can be given
class scope.
I know the static keyword would keep the id private to the source file but
would the const be better with or without static?

The "static const" approach is intended for use in a header file. Since
the value must be explicitly given, it necessary qualifies as a
definition for the object, and therefore provides a separate definition
in each translation unit where the header is #included. Therefore, if it
had external linkage, your program would have undefined behavior due to
providing multiple definitions for the same object. By using the static
keyword, it has internal linkage, and is therefore a separate object in
each translation unit. This might seem to be a waste of space, but
unless the address of the object is taken and used somewhere, no actual
object of that type needs to be created. Any decent compiler should drop
it if it can (for instance, if port_a never gets used anywhere in a
given translation unit).
Would that depend on
whether the definition was in the .c source file or was in a header file
that might be included in multiple object files?

Finally, what would you recommend wrt capitalisation of the identifier name?
As you can see, I've used both lower case and upper case in the different
examples. Aside from the #define I'm not sure what's considered to be more
normal.

There is a convention that such identifiers should be in all-caps - I
think that following that convention is a good idea.
 
D

David Brown

Not really. The 'const' method won't deliver a value that can be used in
many situations where you want an actual compile-time expression. (Array
bounds and case-labels have been mentioned. They will apparently work
for arrays, but might quietly create a VLA instead. They also can't be
used to construct other compile-time expressions.)

The static const will work fine for compile-time expressions (though
with the limitations you noted that they can't be used for array bounds
or case labels).

Since the OP referred to "PORT_A", I have been assuming he is talking
about microcontroller programming and accessing memory-mapped GPIO
registers. It is conceivable, but highly unlikely, that these would be
used for case labels - and even less likely that they would be used for
array bounds.

So you are technically correct, and the limitations are worth
mentioning, but I all three methods will work fine for the typical uses
of such constants in this (assumed) context.
The enum method only works for signed integers (otherwise is the best of
the bunch).

#define has all sorts of issues with scope (using it for a private
constant inside a function, for example, is a bit of a pain). Also any
expression used to define it, could be re-evaluated, with potentially
different results, at each point of use.)

Yes, the different methods all have their advantages and disadvantages.
I don't agree that enum is "the best of the bunch" - it feels too much
of an abuse of the language to me. But it is sometimes the least bad
option.

(The best, of course, would be for C to allow "static const" values to
be used in case labels and array sizes. But that would be a change in
the language.)
Disagree. So long as 'ui16' is itself defined in terms of uint16_t, then
I can't see the problem. It's not as though the name is completely
obscure or misleading.

I agree that type names like ui16 will be portable if they are defined
in terms of uint16_t, but in practice people seldom do so. They usually
come from historic pre-C99 code, or code with a "portability" header
that defines target and compiler specific types and constants. After
all, if you are using <stdint.h>, why would you write "typedef uint16_t
ui16;" when you can just as easily use the standard types? If you think
saving a couple of characters improves your code, you've got big
problems in your development processes.

There are occasions when home-made types like "ui16" make sense (parts
of the Linux kernel use types in that line), and it can make sense to be
consistent with other historic code that uses them - consistency is
important. But in general, for new code, you should stick to the
standard types.
 
J

James Kuyper

On 06/02/2014 06:50 AM, James Harris wrote: ....

I normally use #define in virtually all contexts. In C++, I would use
the const approach, because it has better type safety, and can be given
class scope.

I was in a hurry, and went a little overboard there. When I have a group
of closely related integer constants, and either they all fit in the
range 0-65535 or they all fit in the range -32767 to 32767, I will
usually declare them as enumeration constants of the same enumeration
type. This serves mainly for documentation purposes: it documents that
those constants are related to each other, and anything declared as
having that enumeration type is implicitly documented as having those
values, and no others, as valid values - though there is no
language-level enforcement of that idea.

I'm particularly likely to do this if the values are consecutive
integers starting with 0, because it makes it easy to automate the
counting of the enumeration constants, as in the following example:

typedef enum {
INDEX_250M,
INDEX_500M,
INDEX_1000M_DAY,
INDEX_1000M_NIGHT,
NUM_L1A_RESOLUTIONS
} resolution_index_t;

extern int32 subsamples_at_res[NUM_L1A_RESOLUTIONS];
 
K

Keith Thompson

Malcolm McLean said:
The constant is used once. From then on, it's a variable. Since it
only appears once, it's not important whether it appears as a literal
or not.

The point of the #define is to give the value a name in the source code.
Comments are good, but they very commonly get out of sync with the
actual code.

You wrote:

int main()
{
/* use Ox74 for port A */
intitialiseIO(0x74, "An argument", "Another setting");
}

You already have a typo (Ox74 rather than 0x74); what if you had written
0x73 rather than 0x74? What if the value changes and you update one
occurrence but not the other? Using a #define lets you write the
relevant value just once.
The snag with the #define method is that it's easy to write code which
relies on the #define being a certain value. Then when we change the
definition, it breaks. If the number is a variable, that's still
possible, but it's much less likely.

I'm trying and failing to make sense of that.
Also, you can't use the same subroutine to write a byte to port 0x74
and 0x84, everything is tied to having a single port.

So let the subroutine (or a function as we call it in C) refer to the
#defined value.
 
K

Keith Thompson

Kleuske said:
The 'static'-bit in the last option is out of place. Apart from that,
the last option does not deliver a const expression, so you can't use it
in (for instance) array-sizes or case labels. The other options are
(almost) equivalent.

Correction: it doesn't deliver a *constant* expression. "const" is a
keyword that means, more or less, read-only. "Constant" refers to
expressions that can be, and in many cases must be, evaluated at compile
time (or at least before execution time). The similarity of the terms
can be confusing, but they're quite distinct concepts.
See question 2.22 and 11.8.

Of the comp.lang.c FAQ, http://www.c-faq.com

[...]
 
K

Keith Thompson

BartC said:
Disagree. So long as 'ui16' is itself defined in terms of uint16_t, then I
can't see the problem. It's not as though the name is completely obscure or
misleading.

What's the advantage of using "ui16" rather than the standard "uint16_t"?
 
M

Malcolm McLean

I'm trying and failing to make sense of that.
An expression like PORTA/2 might well break is PORTA is odd. That can happen
if ports is a variable as well, but the programmer is much more likely to test
and be aware if port sis a variable.
So let the subroutine (or a function as we call it in C) refer to the
#defined value.
writeasciiz(int port, char *str)
{
}

can be called on port a and port b, from the same program.


writeasciiztoporta(char *str)
{
writebytes(PORTA, strlen(str) +1);
}

cannot be. So the first function is preferable. It's better to define port A in one place,
then pass it down to the subroutines. This isn't always feasible, for various reasons,
but it's the strategy to adopt where possible.
Also the second function has a dependency on the #define. It's not modular.
 
J

James Kuyper

Disagree. So long as 'ui16' is itself defined in terms of uint16_t, then I
can't see the problem. It's not as though the name is completely obscure or
misleading.

It's entirely reasonable to wonder whether ui16 might be a typedef for
uint_least16_t, uint_fast16_t, or even unsigned short. No two of those
types are guaranteed to be the same, so it matters which of the four it
actually refers to. You can find out, of course, by looking at the
typedef, but if he had used uint16_t, there would have been no need to
look it up.
 
J

James Kuyper

What's the advantage of using "ui16" rather than the standard "uint16_t"?

It allows him to use a name he likes better than the one provided by the
standard. That's more important to him than making it easier for
maintenance programmers to understand what's going on. It's essentially
the same reason some people do

typedef unsigned int uint;
 
R

Richard Bos

Malcolm McLean said:
An expression like PORTA/2 might well break is PORTA is odd. That can happen
if ports is a variable as well, but the programmer is much more likely to test
and be aware if port sis a variable.

That's an assertion, not a proof. And I counter-assert: a programmer is
more likely to check such things if he has the value in front of him
than if he starts with only a variable name.

Richard
 
B

BartC

James Kuyper said:
It's entirely reasonable to wonder whether ui16 might be a typedef for
uint_least16_t, uint_fast16_t, or even unsigned short. No two of those
types are guaranteed to be the same, so it matters which of the four it
actually refers to. You can find out, of course, by looking at the
typedef, but if he had used uint16_t, there would have been no need to
look it up.

There would be no need to look it up anyway, as it wouldn't occur to anyone
who's not a C expert that it might be uint_least16_t or uint_fast16_t
(whatever they might mean).

Although I will concede that someone using one of those last two is more
likely to want to use a short, snazzy typedef instead of cluttering up code
with uinit_least16_t everywhere [typo deliberately left in].

Some of us feel the same about uint16_t (I would use u16 when I needed
something short).
 
J

James Harris

....
Regarding capitalisation, it is a common but not universal custom to
capitalise #define'd macros. (Personally, I rarely capitalise them -
but I believe I am in the minority there.) Opinions are much more
divided about enum constants and static const's. I don't capitalise
them - but some people do. /You/ have to decide if the capitalisation
improves the legibility of /your/ code.

Hard to say. Sometimes sticking to common conventions makes code more
legible. On one hand I prefer all caps for constants because that is
idiomatic and thus easy to recognise. On the other hand there is a semantic
similarity between some constants and the parameters of a function. Indeed,
what is a constant in one piece of code can become a parameter in a later
version and vice versa.

Sometimes I wonder if it wouldn't be better to use all caps consistently for
just macros so that the dangers of their use become more obvious.
Don't use made-up types such as "ui16" - prefer to use the standard C99
types from <stdint.h>, such as uint16_t. They are standardised and
commonly used, and let you write more portable code.

Not always.... One compiler I use does not have stdint definitions. I could
define my own stdint types but to me those names should be left for the
standard headers to use. Using my own names makes clear that these are not
stdints.

Further, a later version of that compiler could include stdints. Keeping to
my own names makes my code independent of such changes.
One other thing to consider here is that you are probably not very
interested in the address of port A. What you are usually interested in
is the contents of the register at that port. A common way to do that
is something like this:

#define portA (*((volatile uint8_t *) 0x0070))

Then you can write things like "portA |= 0x01;" to turn on bit 1 of port
A.

I know what you mean but in this case port_a is an IO port and is not memory
mapped so that would not work. The 16-bit number for port_a eventually gets
passed to an assembly routine which reads or writes the port with an in or
out instruction.

James
 
M

Malcolm McLean

That's an assertion, not a proof. And I counter-assert: a programmer is
more likely to check such things if he has the value in front of him
than if he starts with only a variable name.
If you assume an non-human programmer, then it really doesn't matter what
language or software development methods you use.
If you assume a human programer, you've got to accept that it's difficult to
prove anything about how he is likely to behave.

My experience is that changing a #define to a different value is highly likely to break
something. Passing a different value to a parameterised function, seldom breaks
that function, except for degenerate cases like INT_MIN.
You saw my code break with RAND_MAX

this

#define uniform (rand() / (RAND_MAX + 1.0))

i = uniform() % N;

breaks for certain reasonable values of RAND_MAX.
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top