Related
I am getting this error when I try to execute the following function
src\main.c(41): error: #259: constant value is not known
The code:
uint8_t checksum_tx(uint64_t Data, uint8_t dataLength, uint8_t checksum_len ) {
const uint8_t length = (dataLength + checksum_len - 1) / checksum_len;
//** splitting data into 6-bits subunits . . .
uint8_t res = 0U;
uint8_t dataSubUnit[length];
the line that causes the error
const uint8_t length = (dataLength + checksum_len - 1) / checksum_len;
could someone clarify what is going wrong?
I read somewhere the constants in C must be declared directly, it is not allowed to initialize them, I guess that is what I have done.
This is a limitation of the language... or a feature (depends on who reads it) for the const tagged object. I think your interpretation of the const keyword, applying to a data object with a limited life, should make it immutable for the life of the object, this is, the execution of the block in which it is defined. But the compiler makes the data effectively a constant (like the constant PI) and it must hold the same value for the whole program life, and not on each function invocation. In the case you post, the value of the expression that the constant will have is not known until the function is started and the parameters receive their values (only then can the initialization expression be evalutated), so the constant is not actually a constant (You could provide different values for the function parameters in different call and make the constant value different than in the previous call).
In java, for example, there's a final keyword to mark a data value as constant, and you must preserve (you are not allowed to assign/change values to it) during the life of the object (this meaning since it is created until it is destroyed), but the initial value is determined dynamically, at the time of creation. This is not true in C. A const declared data object must be actually a constant, and it must be initialized by an expression (called a const expression) whose value can be determined at compilation time.
Constants are not real variables: once the code is compiled constants are translated into hard-coded values (immediate operands), or references within a section of the program that cannot be written (usually .rodata).
So, constants can't be assigned a value at run-time. You have to set a value that is known at compilation time, hence a value that does not depend on other variable(s). (e.g. const int length = 42; ).
If you cannot know the value of length before compiling the code, then you need a variable, not a const.
I guess you needed a constant to declare the array dataSubUnit. The usual way to solve this is to declare an array large enough to contain any possible length:
#define MAX_DATA_SUBUNIT_LEN 42
uint8_t checksum_tx(uint64_t Data, ... ) {
uint8_t dataSubUnit[MAX_DATA_SUBUNIT_LEN];
... and to prevent a possible overflow:
uint8_t length = (dataLength + checksum_len - 1) / checksum_len;
if ( length < MAX_DATA_SUBUNIT_LEN ) {
... // proceed
}
else {
// return some relevant error code
}
I have learned from school-books that a typical definition of an enum if like this:
enum weather {
sunny,
windy,
cloudy,
rain,
} weather_outside;
and then declare a var like:
enum weather weather_outside = rain;
My question is, if it is possible to use enumerated constants just by saying e.g. rain which keeps the integer 3, what is exactly the use and the point of having a type-like more complicated deceleration as enum weather weather_outside = rain; to have weather_outside being equal to 3 (since enum values can only be compile-time constants)? Why not just use a const or a macro for it? I am a bit confused whether enums are really necessary at all?!
Enumerations in C are largely for convenience, as they are not strongly typed. They were created to express named options, but limitations of language development and the ways people adopted them for other uses led to the current situation where they are little more than named integer values.
Enumerations support situations where we have various distinct options, such as the weather conditions you show, and want to name them. Ideally, enumerations would be strongly typed, so that rain would not be easily convertible to 3 or vice-versa; writing either int x = rain; or enum weather x = 3; would yield a warning or error from the compiler.
However, there are problems doing this. Consider when we want to write code that processes all values in an enumeration, such as:
for (enum weather i = sunny; i <= rain; i = i+1)
DoSomethingWithWeatherCondition(i);
Take a look at that update expression, i = i+1. It is perfectly natural to an experienced C programmer. (We could write i++, but that is the same thing, and it is spelled out here for illustration.) We know it updates i to the next value. But, when we think about it, what is i+1? i is an enumeration value, and 1 is an int. So we are adding two different things.
To make that work, C treated enumeration values as integers. This allows i+1 to be calculated in the ordinary way as the addition of two integers. Further, then the result is an int, and we have i = some int result, which means we have to allow assigning an int to an enum weather.
Maybe one solution to this would have been to define addition of enumeration values and integers, so that i+1 would not need to treat i as an integer; it would just be define to return the next value in the enumeration after i. But early C development did not do this. It would have been more work, and new features in programming languages were not developed all at once with foresight about what would be useful or what problems might arise. They were often developed bit-by-bit, trying out new things with a little prototype code.
So, enumeration values were integers. Once they were integers, people started using them for purposes beyond the simple original purpose. Enumerations were useful for defining constants that could be used where the compiler needed constant expressions, including array dimensions and initial values for static objects. const did not exist at the time, but it would not have served because, having defined const int x = 3; or even static const int x = 3;, we could not use that x in float array[x];. (Variable-length arrays did not exist at the time, and even now they are not available for static objects.) We also could not use x in int m = 2*x+3; when the definition of m is outside of a function (so it defines a static object). However, if x were defined as an enumeration value rather than an int, it could be used for these purposes.
This lead to enumerations being used in situations where things were not really being enumerated. For example, they are often used for bit-masks of various kinds:
enum
{
DeviceIsReadable = 1,
DeviceIsWriteable = 2,
DeviceSupportsRandomAccess = 4,
DeviceHasFeatureX = 8,
…
}
Once people started using enumerations this way, it was too late to make enumerations strongly typed and define arithmetic on them. These bit masks have to be usable with the bitwise operators |, &, and ^, not just +1. And people were using them for arbitrary constants and arithmetic on them. It would have been too difficult to redefine this part of the C language and change existing code.
So enumerations never developed as properly separate types in C.
This is not the correct syntax:
warning: unused variable 'weather_outside'
This example:
enum light {green, yellow, red};
enum weather { sunny, windy, cloudy, rain,};
enum weather wout;
wout = red; // mismatch
gives a warning with -Wextra:
implicit conversion from 'enum light' to 'enum weather'
This can help prevent errors.
const was not there in the beginning and can be a good alternative; but with an enum you do not have to assign a number - but you can:
enum {sunny, windy, cloudy,
rain = 100, snow}
This must be the most compact way to get two separated regions (0,1,2,100,101).
Your code is invalid. When you write
enum weather {
sunny,
windy,
cloudy,
rain,
} weather_outside;
you already declared a new type called enum weather and a new variable named weather_outside. Doing enum weather weather_outside = rain; will create a new variable with the same name so all compilers I've tried emit errors on that
So the correct way is to remove the first variable definition
enum weather {
// ...
};
enum weather weather_outside = rain;
or use typedef to avoid the use of enum everywhere
typedef enum {
// ...
} weather;
weather weather_outside = rain;
The latter may not be good practice in C due to namespace pollution, and is prohibited in Linux kernel
Back to the main question.
what is exactly the use and the point of having a type-like more complicated deceleration as enum weather weather_outside = rain; to have weather_outside being equal to 3 (since enum values can only be compile-time constants)? Why not just use a const or a macro for it? I am a bit confused whether enums are really necessary at all?!
Semantically 3 doesn't mean anything, and nothing prevents rain from changing value when a new enum member is inserted before it. A named value is always better than a magic number. Besides that way it limits the range of values that weather_outside can accept. If you see or have to do weather_outside = 123 then you know there's something wrong
And to avoid using magical numbers I could also just use a macro as well #define RAIN 3
ALL CAPS ARE HARDER TO READ, and macros are generally discouraged over inline (for functions) or const (for values). But most importantly:
enum allows the debugger to show the current value as name, which is super helpful when debugging. No one knows what 123 means but they surely understand what windy represents
It may be not as useful in this example, but suppose you have a huge enum of 200 different values, how do you know what the 155th item is without counting? The middle items may also be renumbered so their values doesn't correspond to their positions anymore
I'm sure you won't be able to remember all those 200 values when you have 200 const or #define lines. Keep looking at the header file for the value is tedious. And how would you get values of const int my_weather = sunny | windy or #define RAIN (cloudy + floody)? No need to keep track of those with enum. It just works
enum {
sunny = X,
windy = Y,
my_weather = sunny | windy,
cloudy,
floody,
rain = cloudy + rain
}
enum allows you to use the constant in an array declaration
enum array_items {
SPRING,
SUMMER,
FALL,
WINTER,
NUMBER_OF_SEASONS
};
int a[NUMBER_OF_SEASONS] = { 1, 2, 3, 4};
// const int MAX_LENGTH = 4;
// int b[MAX_LENGTH]; /* doesn't work without VLA */
return a[0];
And for an example where the type may be useful, enum text_color { ... }; void set_text_color(enum text_color col); – mediocrevegetable1
I can as well call set_text_color(2) and get no warning what so ever from my compiler!
It's a limitation of C and gcc, because enum is just an integer type in C instead of a real type like in C++, so probably gcc can't do a lot of checks for it. But ICC can warn you about that. Clang also has better warnings than gcc. See:
How to make gcc warn about passing wrong enum to a function
Is there a warning for assigning an enum variable with a value out of the range of the enum?
Warn if invalid value for an enum is passed?
Typesafe enums in C?
Sure enum in C doesn't prevent you from shooting yourself like enum class in C++ but it's much better than macros or constants
Functionally the two methods are equivalent, but enums allow you to better express that something is one of several named choices. It is easier to understand "Rainy" than "3", or "South" rather than "2". It also puts a limit on which values the enumeration can take*.
Using a typedef can help in making the code less verbose:
typedef enum
{
SUNNY,
WINDY,
CLOUDY,
RAIN
} Weather;
Weather weather_outside = RAIN;
switch (weather_outside)
{
case SUNNY:
printf("It's sunny\n");
break;
case WINDY:
printf("It's windy\n");
break;
// ...
}
An additional advantage here is that the compiler may emit a warning if not all enumerated values are handled in the switch, which is wouldn't have if weather_outside was an integer.
Taking a look at function declarations, this:
void takeStep(Direction d)
is more expressive than:
void takeStep(int d)
Of course, you could write int direction, but this is using a variable name to express a type.
[*] It is technically allowed to write Weather weather_outside = 12, as enum values are integer constants, but this should look like a code smell.
Yes, at one level, using an integer variable and a set of preprocessor #defines is just about completely equivalent to using an enum. You achieve the same things: A small number of distinct values, with no necessary numeric interpretation, encoded compactly as (generally) small integers, but represented in source code by more meaningful symbolic names.
But the preprocessor is, to some extent, a kludge, and modern practice recommends avoiding its use when possible. And enums, since they are known to the compiler proper, have a number of additional advantages:
type safety — the compiler can warn you if you use values that don't belong (e.g. weather = green)
debugging — a debugger can show you the value of an enumeration as its symbolic name, not as an inscrutable number
additional warnings — the compiler can warn you if you do a switch on an enumeration but forget one of the cases
Enums are integers and can be used as constant expressions.
enum weather {
sunny,
windy,
cloudy,
rain,
} weather_outside;
int main(void)
{
int weather = cloudy;
printf("%d\n", rain);
printf("`weather`==%d\n", weather);
}
https://godbolt.org/z/939KvreEY
I've noticed in linux kernel source code, that some enums definition have first member of enum explicitly initialized with 0 as follows:
enum i2c_adapter_type {
I2C_ADAPTER_SMBUS = 0,
I2C_ADAPTER_VGADDC,
I2C_ADAPTER_PANEL,
I2C_ADAPTER_DESIGNWARE_0,
I2C_ADAPTER_DESIGNWARE_1,
};
Code snippet available here at chromeos_laptop.c:56
https://github.com/torvalds/linux/blob/master/drivers/platform/chrome/chromeos_laptop.c
Can someone explain me, what is the purpose of doing such a construct ? I know that first enum member is guaranteed to be 0 by later C standards (ANSI, C99 and so on), so why people are doing this ?
While this is not a syntactic necessity, usually the zero is inserted by the programmer to make the semantics of the enum members clear to the reader. A reason could be that the enums are used as index to an array, or as ID for some external resource. It is a hint that changing the enum (removing, adding, reordering) may have repercussions, though an additional comment is a good idea. Which is there in this case, in line 55 where it belongs:
/* Keep this enum consistent with i2c_adapter_names */
This pattern could be extended to explicitely providing the values for each member, e. g. if it may happen that entries are removed when they become obsolete and you want to keep the gap or the absolute values for some reason:
static const char *i2c_adapter_names[] = {
"SMBus I801 adapter",
"i915 gmbus vga",
"<unsupported>",
"Synopsys DesignWare I2C adapter",
"Synopsys DesignWare I2C adapter",
};
enum i2c_adapter_type {
I2C_ADAPTER_SMBUS = 0,
I2C_ADAPTER_VGADDC = 1,
/*I2C_ADAPTER_PANEL = 2,*/
I2C_ADAPTER_DESIGNWARE_0 = 3,
I2C_ADAPTER_DESIGNWARE_1 = 4,
};
Addition
As for UNPROBED and its siblings in i2c_peripheral_state, this has also a special meaning: That enum is used in the struct i2c_peripheral, and 0 is the default value when you use memset(…, 0, sizeof(…)) (I'm not a fan of it due to possible side effects) or an initializer list (should be preferred) to initialize the struct. So the state UNPROBED is the default when no other value is explicitely assigned, as can be seen in a couple of places where instances of this struct are assigned, e. g. line 429.
And a protip as bonus: It's much easier to follow the usage of identifiers with the Linux cross reference.
I am relatively knew to C and only learning pieces of it to publish a Pebble C/PebbleKitJS app to track buses. So far I have the data being processed on a Node server, and I am getting ready to have the data processed by a JS File. MY one problem however lies within the C Code.
This code process data stored in a Key Dictionary sent from JS and assigns it to a variable for use below. By using #define var 9, I can successfully have the .high value set to 9. But through an int var, it fails and throws the error:initializer element not constant?? .
What does this error mean, and what exactly is the difference between static and constant if i don't define it. apparently static vars don't return anything? Some help would be much appreciated.
UPDATE: The problem still isn't fixed. The following new error message occurs in addition to the initializer one. error: (near initialization for 's_data_points[0].high')
int key0_buffer;
void process_tuple(Tuple *t)
{
//Get key
int key = t->key;
//Get integer value, if present
int value = t->value->int32;
//Get string value, if present
char string_value[32];
strcpy(string_value, t->value->cstring);
//Decide what to do
switch(key) {
case key_0:
//Location received
key0_buffer = value;
break;
}
}
static WeatherAppDataPoint s_data_points[] = {
{
.city = "San Diego",
.description = "surfboard :)",
.icon = WEATHER_APP_ICON_GENERIC_WEATHER,
.current = 110,
.high = key0_buffer,
.low = 9,
},
};
Try this instead:
enum { key0_buffer = 9 };
C doesn't provide for runtime computations while initializing global variables. (The concept does exist as a C++ feature called "dynamic initialization.")
The execution model is that it can store all the bytes of global variables in ROM, and then copy any modifiable variables into RAM together with a single memcpy. Assigning one global to another would be more complicated.
#define allows you to substitute the text 9, which is a constant expression.
Many frown upon using text substitutions to avoid variables as primitive, unnecessarily low-level, and potentially inefficient. In this case though, the results should be the same.
In C, enum constants have type int, so they are a suitable substitute. You're out of luck for other types, though.
There is a key difference between the code in your function and the code lower down that defines the static variable. The function code is executable -- these lines are going to get run when the function is called.
Your static declaration of WeatherAppDataPoint is just telling the compiler to create the static variable. The initialization values you are placing in that declaration tell the compiler what value to initialize this data to. That is why they must be constant -- these are the values that get loaded in before anything gets executed.
The #define statement just tells the preprocessor to replace all instances of "var" with the string "9". It is literally the same as a cut and paste operation in a text editor. This gets done before the compiler ever sees the code. This is why you have no problems with that; the compiler sees a literal constant, just as if you'd manually typed the "9" directly into the source code.
In 'C', variables don't return things, they store them. Only functions return things. If you want to have an integer declared somewhere, and then assign it to the value of your static variable, that is an actual executable line of code that will need to occur inside of a function somewhere (ie inside of your process_tuple function). The lines of code below the static declaration aren't executed at run time, they just setup the initial state of the program and tell the compiler how big the variable is.
Which one is better to use among the below statements in C?
static const int var = 5;
or
#define var 5
or
enum { var = 5 };
It depends on what you need the value for. You (and everyone else so far) omitted the third alternative:
static const int var = 5;
#define var 5
enum { var = 5 };
Ignoring issues about the choice of name, then:
If you need to pass a pointer around, you must use (1).
Since (2) is apparently an option, you don't need to pass pointers around.
Both (1) and (3) have a symbol in the debugger's symbol table - that makes debugging easier. It is more likely that (2) will not have a symbol, leaving you wondering what it is.
(1) cannot be used as a dimension for arrays at global scope; both (2) and (3) can.
(1) cannot be used as a dimension for static arrays at function scope; both (2) and (3) can.
Under C99, all of these can be used for local arrays. Technically, using (1) would imply the use of a VLA (variable-length array), though the dimension referenced by 'var' would of course be fixed at size 5.
(1) cannot be used in places like switch statements; both (2) and (3) can.
(1) cannot be used to initialize static variables; both (2) and (3) can.
(2) can change code that you didn't want changed because it is used by the preprocessor; both (1) and (3) will not have unexpected side-effects like that.
You can detect whether (2) has been set in the preprocessor; neither (1) nor (3) allows that.
So, in most contexts, prefer the 'enum' over the alternatives. Otherwise, the first and last bullet points are likely to be the controlling factors — and you have to think harder if you need to satisfy both at once.
If you were asking about C++, then you'd use option (1) — the static const — every time.
Generally speaking:
static const
Because it respects scope and is type-safe.
The only caveat I could see: if you want the variable to be possibly defined on the command line. There is still an alternative:
#ifdef VAR // Very bad name, not long enough, too general, etc..
static int const var = VAR;
#else
static int const var = 5; // default value
#endif
Whenever possible, instead of macros / ellipsis, use a type-safe alternative.
If you really NEED to go with a macro (for example, you want __FILE__ or __LINE__), then you'd better name your macro VERY carefully: in its naming convention Boost recommends all upper-case, beginning by the name of the project (here BOOST_), while perusing the library you will notice this is (generally) followed by the name of the particular area (library) then with a meaningful name.
It generally makes for lengthy names :)
In C, specifically? In C the correct answer is: use #define (or, if appropriate, enum)
While it is beneficial to have the scoping and typing properties of a const object, in reality const objects in C (as opposed to C++) are not true constants and therefore are usually useless in most practical cases.
So, in C the choice should be determined by how you plan to use your constant. For example, you can't use a const int object as a case label (while a macro will work). You can't use a const int object as a bit-field width (while a macro will work). In C89/90 you can't use a const object to specify an array size (while a macro will work). Even in C99 you can't use a const object to specify an array size when you need a non-VLA array.
If this is important for you then it will determine your choice. Most of the time, you'll have no choice but to use #define in C. And don't forget another alternative, that produces true constants in C - enum.
In C++ const objects are true constants, so in C++ it is almost always better to prefer the const variant (no need for explicit static in C++ though).
The difference between static const and #define is that the former uses the memory and the later does not use the memory for storage. Secondly, you cannot pass the address of an #define whereas you can pass the address of a static const. Actually it is depending on what circumstance we are under, we need to select one among these two. Both are at their best under different circumstances. Please don't assume that one is better than the other... :-)
If that would have been the case, Dennis Ritchie would have kept the best one alone... hahaha... :-)
In C #define is much more popular. You can use those values for declaring array sizes for example:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
ANSI C doesn't allow you to use static consts in this context as far as I know. In C++ you should avoid macros in these cases. You can write
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
and even leave out static because internal linkage is implied by const already [in C++ only].
Another drawback of const in C is that you can't use the value in initializing another const.
static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;
// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND
* NUMBER_OF_HANDS;
Even this does not work with a const since the compiler does not see it as a constant:
static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!
I'd be happy to use typed const in these cases, otherwise...
If you can get away with it, static const has a lot of advantages. It obeys the normal scope principles, is visible in a debugger, and generally obeys the rules that variables obey.
However, at least in the original C standard, it isn't actually a constant. If you use #define var 5, you can write int foo[var]; as a declaration, but you can't do that (except as a compiler extension" with static const int var = 5;. This is not the case in C++, where the static const version can be used anywhere the #define version can, and I believe this is also the case with C99.
However, never name a #define constant with a lowercase name. It will override any possible use of that name until the end of the translation unit. Macro constants should be in what is effectively their own namespace, which is traditionally all capital letters, perhaps with a prefix.
#define var 5 will cause you trouble if you have things like mystruct.var.
For example,
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
The preprocessor will replace it and the code won't compile. For this reason, traditional coding style suggest all constant #defines uses capital letters to avoid conflict.
It is ALWAYS preferable to use const, instead of #define. That's because const is treated by the compiler and #define by the preprocessor. It is like #define itself is not part of the code (roughly speaking).
Example:
#define PI 3.1416
The symbolic name PI may never be seen by compilers; it may be removed by the preprocessor before the source code even gets to a compiler. As a result, the name PI may not get entered into the symbol table. This can be confusing if you get an error during compilation involving the use of the constant, because the error message may refer to 3.1416, not PI. If PI were defined in a header file you didn’t write, you’d have no idea where that 3.1416 came from.
This problem can also crop up in a symbolic debugger, because, again, the name you’re programming with may not be in the symbol table.
Solution:
const double PI = 3.1416; //or static const...
I wrote quick test program to demonstrate one difference:
#include <stdio.h>
enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};
#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32
int main(int argc, char *argv[]) {
printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);
return(0);
}
This compiles with these errors and warnings:
main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
^
Note that enum gives an error when define gives a warning.
The definition
const int const_value = 5;
does not always define a constant value. Some compilers (for example tcc 0.9.26) just allocate memory identified with the name "const_value". Using the identifier "const_value" you can not modify this memory. But you still could modify the memory using another identifier:
const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.
This means the definition
#define CONST_VALUE 5
is the only way to define a constant value which can not be modified by any means.
Although the question was about integers, it's worth noting that #define and enums are useless if you need a constant structure or string. These are both usually passed to functions as pointers. (With strings it's required; with structures it's much more efficient.)
As for integers, if you're in an embedded environment with very limited memory, you might need to worry about where the constant is stored and how accesses to it are compiled. The compiler might add two consts at run time, but add two #defines at compile time. A #define constant may be converted into one or more MOV [immediate] instructions, which means the constant is effectively stored in program memory. A const constant will be stored in the .const section in data memory. In systems with a Harvard architecture, there could be differences in performance and memory usage, although they'd likely be small. They might matter for hard-core optimization of inner loops.
Don't think there's an answer for "which is always best" but, as Matthieu said
static const
is type safe. My biggest pet peeve with #define, though, is when debugging in Visual Studio you cannot watch the variable. It gives an error that the symbol cannot be found.
Incidentally, an alternative to #define, which provides proper scoping but behaves like a "real" constant, is "enum". For example:
enum {number_ten = 10;}
In many cases, it's useful to define enumerated types and create variables of those types; if that is done, debuggers may be able to display variables according to their enumeration name.
One important caveat with doing that, however: in C++, enumerated types have limited compatibility with integers. For example, by default, one cannot perform arithmetic upon them. I find that to be a curious default behavior for enums; while it would have been nice to have a "strict enum" type, given the desire to have C++ generally compatible with C, I would think the default behavior of an "enum" type should be interchangeable with integers.
A simple difference:
At pre-processing time, the constant is replaced with its value.
So you could not apply the dereference operator to a define, but you can apply the dereference operator to a variable.
As you would suppose, define is faster that static const.
For example, having:
#define mymax 100
you can not do printf("address of constant is %p",&mymax);.
But having
const int mymax_var=100
you can do printf("address of constant is %p",&mymax_var);.
To be more clear, the define is replaced by its value at the pre-processing stage, so we do not have any variable stored in the program. We have just the code from the text segment of the program where the define was used.
However, for static const we have a variable that is allocated somewhere. For gcc, static const are allocated in the text segment of the program.
Above, I wanted to tell about the reference operator so replace dereference with reference.
We looked at the produced assembler code on the MBF16X... Both variants result in the same code for arithmetic operations (ADD Immediate, for example).
So const int is preferred for the type check while #define is old style. Maybe it is compiler-specific. So check your produced assembler code.
I am not sure if I am right but in my opinion calling #defined value is much faster than calling any other normally declared variable (or const value).
It's because when program is running and it needs to use some normally declared variable it needs to jump to exact place in memory to get that variable.
In opposite when it use #defined value, the program don't need to jump to any allocated memory, it just takes the value. If #define myValue 7 and the program calling myValue, it behaves exactly the same as when it just calls 7.