I am writing some embedded code to interface with an external device over SPI. The device has several registers of varying length and to help keep things straight I have defined the following structure
typedef struct
{
uint16_t Signed :1; // Register is signed or unsigned
uint16_t CommLengthBytes :3; // The width of the register in bytes
uint16_t Address :12; // Register address
}ts_register;
I have then defined each register in my sources as follows
static const ts_register SAGCYC = {0, 1, 0x000};
static const ts_register DISNOLOAD = {0, 1, 0x001};
static const ts_register LCYCMODE = {0, 1, 0x004};
static const ts_register IRMSA = {0, 4, 0x31A};
static const ts_register IRMSB = {0, 4, 0x31B};
static const ts_register VRMS = {0, 4, 0x31C};
etc.
I have a function that will take a pointer to an array of ts_registers and queue up the SPI transfers required to read all of the registers in the array and call a callback function to handle the reply
My issue comes when I try to make the array of ts_registers that I want to read as follows:
ts_register regs_to_read[3] = {VRMS, IRMSA, IRMSB};
This generates the error: "expression must have a constant value" 3 times (once per array element).
Since they are defined as constants, what have I overlooked?
Since they are defined as constants, what have I overlooked?
In C objects declared with the const modifier aren't true constants. A better name for const would probably be readonly - what it really means is that the compiler won't let you change it. And you need true constants to initialize objects with static storage (I suspect regs_to_read is global).
You could try assigning regs_to_read in a function called before anything else uses that array.
const doesn't make them constants at compile time. Make them #defines and the compiler
will be happy.
I think this may be a compiler issue, and it would be helpful to know your platform and how you are building this code. I just took most of your code, doctored it up to compile it, and compiled on Linux using gcc. There were no warnings.
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
typedef struct
{
uint16_t Signed :1; // Register is signed or unsigned
uint16_t CommLengthBytes :3; // The width of the register in bytes
uint16_t Address :12; // Register address
}ts_register;
int main(int argc, char **argv) {
static const ts_register SAGCYC = {0, 1, 0x000};
static const ts_register DISNOLOAD = {0, 1, 0x001};
static const ts_register LCYCMODE = {0, 1, 0x004};
static const ts_register IRMSA = {0, 4, 0x31A};
static const ts_register IRMSB = {0, 4, 0x31B};
static const ts_register VRMS = {0, 4, 0x31C};
ts_register regs_to_read[3] = {VRMS, IRMSA, IRMSB};
return(0);
}
Have you tried casting the values? It's not always the best thing to do, but will get you around the error.
Have you considered creating #define entries?
Also, please be aware consts take a bit of getting used to in C. They do not always behave the way you might expect.
If regs_to_read is declared in any function scope, the error would be cleared.
Related
I'm new to c, and currently making chess program.
There's the code.
#include <stdio.h>
int spider[8][2] = {{0, 1}, {1, 1}, {1, 0}, {1, -1},
{0, -1}, {-1, -1}, {1, 0}, {-1, 1}};
int jump[8][2] = {{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-2, -1}, {-1, -2}, {-1, 2}, {-2, 1}};
typedef struct
{
int color;
int type;
char symbol;
int unit[8][2];
} piece;
void add_piece(piece piece, int color, int type, char symbol, int unit[8][2])
{
piece.color = color;
piece.type = type;
piece.symbol = symbol;
for(int i = 0; i < 8; i++)
{
for(int j = 0; j < 2; j++)
{
piece.unit[i][j] = unit[i][j];
}
}
}
int main(void)
{
piece wk;
add_piece(wk, 0, 0, 'K', spider);
printf("%d", wk.color);
return 0;
}
expected output : 0
console output : 8388608
I found that 8GB = 8388608KB.
What's the meaning of that output?
Is there any problem of initializing property of structure or elsewhere?
Can you see some not good habits in this code?
Remember that in C all arguments are passed by value.
That means the value of the argument in the call is copied into the functions argument value.
So in the call
add_piece(wk, 0, 0, 'K', spider);
the value (current uninitialized contents) of the structure object wk will be copied into the argument variable piece inside the add_piece function.
When you modify the local argument variable, for example through an assignment like
piece.color = color;
you only modify the local copy. The original value used in the call will not be modified.
You can solve this in two ways:
Return the structure, and assign to a variable when doing the call; Or
Emulate pass by reference by using pointers.
Option number 2 is the most common it seems, so I will show it here.
First you need to modify the function to take a pointer to the structure object:
void add_piece(piece *piece, int color, int type, char symbol, int unit[8][2])
{
...
}
Then you need to use the "pointer to structure" syntax using the "arrow" to access structure members:
piece->color = color;
piece->type = type;
// And so on for all access to piece members...
Finally, when you call the function you need to pass a pointer to the wk variable, using the "pointer to" or "address of" operator &:
add_piece(&wk, 0, 0, 'K', spider);
I described the pass by reference emulation because it's so common and because you need to learn to recognize it. For your specific case I would rather recommend that you define a new structure object in the function and return it:
piece add_piece(int color, int type, char symbol, int unit[8][2])
{
piece piece;
piece.color = color;
piece.type = type;
// And so on, much like your current function...
// Return the structure
return piece;
}
Now you can use it as such:
piece wk = add_piece(0, 0, 'K', spider);
Note that sometimes it's not possible, or desirable, to return structures like this, and you still need to emulate pass by reference. So you need to learn both ways.
Lastly a note about the output you get: Uninitialized local (non-static) variables really are uninitialized. They will have indeterminate (read: garbage) values.
Since in your current code doesn't really initialize the original wk structure object, all its members will have such indeterminate values.
Depending on type and use, using such indeterminate values could lead to undefined behavior.
I am writing some embedded code to interface with an external device over SPI. The device has several registers of varying length and to help keep things straight I have defined the following structure
typedef struct
{
uint16_t Signed :1; // Register is signed or unsigned
uint16_t CommLengthBytes :3; // The width of the register in bytes
uint16_t Address :12; // Register address
}ts_register;
I have then defined each register in my sources as follows
static const ts_register SAGCYC = {0, 1, 0x000};
static const ts_register DISNOLOAD = {0, 1, 0x001};
static const ts_register LCYCMODE = {0, 1, 0x004};
static const ts_register IRMSA = {0, 4, 0x31A};
static const ts_register IRMSB = {0, 4, 0x31B};
static const ts_register VRMS = {0, 4, 0x31C};
etc.
I have a function that will take a pointer to an array of ts_registers and queue up the SPI transfers required to read all of the registers in the array and call a callback function to handle the reply
My issue comes when I try to make the array of ts_registers that I want to read as follows:
ts_register regs_to_read[3] = {VRMS, IRMSA, IRMSB};
This generates the error: "expression must have a constant value" 3 times (once per array element).
Since they are defined as constants, what have I overlooked?
Since they are defined as constants, what have I overlooked?
In C objects declared with the const modifier aren't true constants. A better name for const would probably be readonly - what it really means is that the compiler won't let you change it. And you need true constants to initialize objects with static storage (I suspect regs_to_read is global).
You could try assigning regs_to_read in a function called before anything else uses that array.
const doesn't make them constants at compile time. Make them #defines and the compiler
will be happy.
I think this may be a compiler issue, and it would be helpful to know your platform and how you are building this code. I just took most of your code, doctored it up to compile it, and compiled on Linux using gcc. There were no warnings.
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
typedef struct
{
uint16_t Signed :1; // Register is signed or unsigned
uint16_t CommLengthBytes :3; // The width of the register in bytes
uint16_t Address :12; // Register address
}ts_register;
int main(int argc, char **argv) {
static const ts_register SAGCYC = {0, 1, 0x000};
static const ts_register DISNOLOAD = {0, 1, 0x001};
static const ts_register LCYCMODE = {0, 1, 0x004};
static const ts_register IRMSA = {0, 4, 0x31A};
static const ts_register IRMSB = {0, 4, 0x31B};
static const ts_register VRMS = {0, 4, 0x31C};
ts_register regs_to_read[3] = {VRMS, IRMSA, IRMSB};
return(0);
}
Have you tried casting the values? It's not always the best thing to do, but will get you around the error.
Have you considered creating #define entries?
Also, please be aware consts take a bit of getting used to in C. They do not always behave the way you might expect.
If regs_to_read is declared in any function scope, the error would be cleared.
For efficient code maintenance I need to make sure that the value at index 0 of an array is a specific predefined value. The following code doesn't work:
#define SPECIFIC_ADDR_IDX 0
#define SPECIFIC_ADDR 8
#define NOT_SPECIFIC_ADDR1 12
#define NOT_SPECIFIC_ADDR2 16
typedef struct _struct_s
{
const uint16_t addr; // addresses are constant and are not mutable
uint32_t val;
} struct_s;
struct_s globArr[] =
{
{.addr = SPECIFIC_ADDR, .val = 0},
{.addr = NOT_SPECIFIC_ADDR1, .val = 0},
{.addr = NOT_SPECIFIC_ADDR2, .val = 0},
};
// make sure the address at the SPECIFIC_ADDR_IDX is SPECIFIC_ADDR
_Static_assert(globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR, " Illegal!");
It gives the following compilation error:
error: expression in static assertion is not constant
_Static_assert (globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR, " Illegal!");
~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
The addr is defined as const uint16_t, so I was thinking that its value is known at the compilation time.
Is there an efficient way to perform such check at the compilation time?
Clarification: I understand that in this way I can't use the _Static_assert, const doesn't make a variable's value known at the compilation time. What I am asking is if anyone knows any kind of trick to deal with such issues.
The satisfying solution was proposed by Kamil Cuk. The initialization can be done with specifying the index:
struct_t globArr[] =
{
[SPECIFIC_ADDR_IDX] = { .addr = SPECIFIC_ADDR, .val = 0 },
{.addr = NOT_SPECIFIC_ADDR1, .val = 0},
{.addr = NOT_SPECIFIC_ADDR2, .val = 0},
};
In such case if there would be additional initialization of entry at index [SPECIFIC_ADDR_IDX], the compiler will issue a warning (not guaranteed but most compilers will). Just make sure to compile with warning=error option ON.
You can just specify the intex in initialization:
#define SPECIFIC_ADDR_IDX 0
#define SPECIFIC_ADDR 8
#define NOT_SPECIFIC_ADDR1 12
#define NOT_SPECIFIC_ADDR2 16
typedef struct _struct_s
{
const uint16_t addr; // addresses are constant and are not mutable
uint32_t val;
} struct_t;
struct_t globArr[] =
{
[SPECIFIC_ADDR_IDX] = { .addr = SPECIFIC_ADDR, .val = 0 },
{.addr = NOT_SPECIFIC_ADDR1, .val = 0},
{.addr = NOT_SPECIFIC_ADDR2, .val = 0},
};
Anyway you need to do runtime assertion, so use assert:
void globArr_unittest(void) {
assert(globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR);
}
static_assert needs a constant expression. You can't write static assertions on variables values. Even if you did static const struct_t globArr[] still globArr value is not a constant expression. The C language has no constexpr (or consteval) specifier like C++. So, sadly, you can't do that in C.
const is just a modifier, it says that the variable can't be modified through this handle. const variables can be modified and are not immutable in C.
It's just like you can't do:
#if globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR
the same you can't
static_assert(globArr[SPECIFIC_ADDR_IDX].addr == SPECIFIC_ADDR, "");
What is a constant expression is probably nicely enumerated at cppreference. The result of array subscript [] and member access . operators is not a constant expression, therefore it can't be used in static assertion.
In a C library project of mine I have an enum that lists all possible types a piece of data handled by the library can be:
// lib.h
enum types {
VOID,
INT,
FLOAT,
CONST_INT,
CONST_FLOAT
}
The code will be compiled into a shared library. In future versions of the library, I will be required to insert new entries into the enum and reorder existing ones. AFAIK this breaks ABI compatibility since the enum is not transformed into a set of symbols that ends up in the library, but rather causes whatever integer the compiler assigns to each entry to be hardcoded. Is that the case?
If so, would it be preferable to instead use constant global variables such that they make an appearance in the symbol table and I thus can change both order and the value assigned to each?
// lib.c
const int VOID = 1;
const int INT = 2;
const int FLOAT = 3;
const int CONST_INT = 4;
const int CONST_FLOAT = 5;
// lib.h
extern const int VOID;
extern const int INT;
extern const int FLOAT;
extern const int CONST_INT;
extern const int CONST_FLOAT;
You're right. If you plan to expand the enumered variables it could make incompatible the future versions of your libraries.
If you take a look to some of the most relevant sw around (i.e. MS or linux headers) you can see that the solutions adopted are mainly two:
Use defines
Still use enum, but assign a value per each entry
The latter makes the use of enum quite equal to defines, but retains the properties of enums.
In your case it could be:
// lib.h
enum types {
VOID = 0,
INT = 1,
FLOAT = 10,
CONST_INT = 12,
CONST_FLOAT = 13
}
Then when in future you will add other codes:
// lib.h
enum types {
VOID = 0,
INT = 1,
CUSTOM1 = 3,
FLOAT = 10,
CUSTOM2 = 11,
CONST_INT = 12,
CONST_FLOAT = 13,
CUSTOM4 = 20
}
The use of constant globals could have some problems, even to be optimized out and be replaced by constants.
best way is to place the enum in the header file for that library.
Changing the enum will still require re-compiling/linking etc of the library (no getting around that),
To assure each label in the enum has the specific value, the enum can be written similar to:
enum
{
label1 = 0,
label2 = 1,
label3 = 20,
label4 = 5
};
the results in anonymous enum, where each of the labels are visible and have the appropriate value.
When I wrote a question regarding PC-Lint, I had made an assumption that the following initialization is valid in C99. #JoachimPileborg mentioned that it may not be and I haven't been able to find any information that provides a good example one way or another. I know that it compiles and behaves as I expect, I would just like to know for certain that it is proper C99 code.
Is this a valid way to initialize the following union in C99?
typedef union
{
struct
{
unsigned int a : 4;
unsigned int b : 4;
unsigned int c : 4;
unsigned int d : 4;
} bits;
unsigned short value;
} My_Value;
int main (void)
{
My_value test[] =
{
{
.bits.a = 2,
.bits.b = 3,
.bits.c = 2,
.bits.d = 3,
},
{
.bits.a = 1,
.bits.b = 1,
.bits.c = 1,
.bits.d = 0,
},
};
/* Do something meaningful. */
return 0;
}
Looks sane... if your tame compiler doesn't complain with standards compliance cranked way up, I'd use it. Much more worrying is that you presumably are trying to overlay value and bits, and stuffing data into one alternative of an union an taking it out of the other is undefined. Endianness aside, the union will probably use up a full word, and very well could have the value at one end and the bits at the other (depending on available instructions and their convenience or timing). The standards declare this undefined precisely to give implementations such leeway.