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.
Related
Suppose I have a global variable, and I want to assign another variable to it. I've found out that you can assign another value to a global variable inside a function:
int i = 8;
int main(void)
{
i = 9; /* Modifies i */
return 0;
}
However, assignment of the global variable outside of a function does not work!
int i = 8;
i = 9; /* Compiler error */
int main(void)
{
return 0;
}
I get the following error message:
warning: data definition has no type or storage class
warning: type defaults to 'int' in declaration of 'i'
error: redefinition of 'i'
note: previous definition of 'i' was here
int i = 8;
^
Why is this happening?
This is a definition of a global variable, with the optional initialisation to a specific value:
int i = 8;
Note that it is not code which gets ever executed, the variable will just be set up to initially contain the 8. Either consider it "magic" (a helpful model for many things not really defined by the standard) or think of tables with values being copied to memory locations before any code is executed.
This is a piece of code which has no "frame" in which it is executed.
(Or you intend it to be. The compiler is of other opinion, see below.)
i = 9;
There is no function containing it. It is not clear when it should be executed. That is what the compiler does not like.
In C, all code has to be inside a function and will only be executed if that function is called, e.g. from main().
Other language, mostly those which execute "scripts" by interpreting them (instead of code being turned into executeables, e.g. by a compiler) allow to have code anywhere. C is different.
The compiler sees this differently:
i = 9;
it is not inside a function, so it cannot be code
it looks like a variable definition, assuming that you mean it to be an int, i.e. the default
but relying on defaults is not a good idea, so warn about missing type and that the default is used
also, if it is a definition, then it is the second one for i, now that is really wrong, so show an error and fail the compiling
just to be helpful, mention where the first definition of i is
That is how to read the compiler output you have quoted.
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
}
Suppose I have a global variable, and I want to assign another variable to it. I've found out that you can assign another value to a global variable inside a function:
int i = 8;
int main(void)
{
i = 9; /* Modifies i */
return 0;
}
However, assignment of the global variable outside of a function does not work!
int i = 8;
i = 9; /* Compiler error */
int main(void)
{
return 0;
}
I get the following error message:
warning: data definition has no type or storage class
warning: type defaults to 'int' in declaration of 'i'
error: redefinition of 'i'
note: previous definition of 'i' was here
int i = 8;
^
Why is this happening?
This is a definition of a global variable, with the optional initialisation to a specific value:
int i = 8;
Note that it is not code which gets ever executed, the variable will just be set up to initially contain the 8. Either consider it "magic" (a helpful model for many things not really defined by the standard) or think of tables with values being copied to memory locations before any code is executed.
This is a piece of code which has no "frame" in which it is executed.
(Or you intend it to be. The compiler is of other opinion, see below.)
i = 9;
There is no function containing it. It is not clear when it should be executed. That is what the compiler does not like.
In C, all code has to be inside a function and will only be executed if that function is called, e.g. from main().
Other language, mostly those which execute "scripts" by interpreting them (instead of code being turned into executeables, e.g. by a compiler) allow to have code anywhere. C is different.
The compiler sees this differently:
i = 9;
it is not inside a function, so it cannot be code
it looks like a variable definition, assuming that you mean it to be an int, i.e. the default
but relying on defaults is not a good idea, so warn about missing type and that the default is used
also, if it is a definition, then it is the second one for i, now that is really wrong, so show an error and fail the compiling
just to be helpful, mention where the first definition of i is
That is how to read the compiler output you have quoted.
This is not a problem, but I'd like to understand the following behaviour:
int main() {
// This does not work
int const a;
a = 50;
// This work
int const a = 50;
}
Why does the compiler throw the following error:
main.c:4:7: error: assignment of read-only variable ‘a’
I don't understand why even the initialization is forbidden. The line 3 has no translation in the assembly language. Does the compiler not detect that the line 4 is the first affectation (translation issue: I meant "assignment")?
EDIT: Ok, so let's say this is how the C language is. But this can be a problem because when I use C89, the declarations must be at the top of the functions and I can't use constant variables because assignments must be placed after the declarations . The only solution is to declare non-const variables or initialize all the variables. I find this "dirty".
I don't understand why even the initialization is forbidden.
The key is in the error message:
error: assignment of read-only variable ‘a’
It isn't an initialization, it is an assignment. An assignment modifies an existing object, and that is not permitted if said object is const.
This, on the other hand, despite using the = syntax, is an initialization:
int const a = 50;
Because it's a constant!
a = 50; is an assignment, not an initialisation.
int const a; essentially sets a to an indeterminant value (which you should never read by the way). Perhaps your compiler will warn you of this if you ask it nicely.
" Does the compiler not detect that the line 4 is the first affectation?"
It could, in this simple case. However the rules are written to cover all cases of initialization.
Compiler looks at one file at a time. A program may be composed of many files. The language allows variables to be declared in one translation unit (file) and used in another.
The rules are written so they take care of all such cases
I have a C program which has always used hard coded define statements for a few settings. Example...
#define TRIGGER_TIMEOUT 50000
This has worked just fine. Now, this setting is to become adjustable. Example...
g_ulTriggerTimeout = ReadEEPROM(TRIGGER_TIMEOUT_OFFSET);
If persistent storage is detected (EEPROM) then the value will be read in and used. So, my safe literal value is now at risk of being corrupted (inadvertently written changed). I need to make this variable a constant, however I also need to read in the initial values from EEPROM. How is this scenario typically solved?
This is IMHO one of those cases where bending the rules is ok.
const int g_ulTriggerTimeout; //declare somewhere making sure it is in a writable section (see comment below from undur)
/* later */
//just for this assignment make it modifiable
*((int*) &g_ulTriggerTimeout) = ReadEEPROM(TRIGGER_TIMEOUT_OFFSET);
I do not like bending the rules (invoking undefinded behaviour) like RedX's answer does. Therefore, I now give a C standard-compliant solution. The price is having a constant function instead of a constant variable.
int g_ulTriggerTimeout()
{
static int done; // statically initialized to zero
static int value;
if( !done )
{
value = haveEEPROM ? ReadEEPROM(TRIGGER_TIMEOUT_OFFSET)
: TRIGGER_TIMEOUT;
done = 1;
}
return value;
}
Following is my first answer, it is however only valid in C++:
const int g_ulTriggerTimeout = haveEEPROM ? ReadEEPROM(TRIGGER_TIMEOUT_OFFSET)
: TRIGGER_TIMEOUT;
You cannot (portably/with C).
If you have to modify the variable in your program (by reading it from EEPROM), you cannot lock it against modification by your program.
There might be platform specific means to achieve that though.