Let's suppose, I have a following structure:
struct my_struct {
uint32_t bf1 : 3;
uint32_t bf2 : 5;
uint32_t bf3 : 16;
uint32_t bf4 : 8;
};
and the following enum:
enum bf1_values {
Val1 = 0x0;
Val2 = 0x4;
Val3 = 0x7;
};
in addition, getter and setter functions for bf1:
uint32_t bf1_getter() {
return global_struct.bf1; // cast value to (uint32_t)?
}
void bf1_setter(enum bf1_values val) {
global_struct.bf1 = val; // cast enum to (uint32_t)?
}
Should I use the typecasting in getter and setter functions for safety?
EDIT:
The structure is supposed to be sent to HW.
EDIT2:
What I want to achieve is to be really sure that enum will be correctly written to a bitfield, and correctly read from bitfield.
No need for casting here - the assignments are already 'safe' insofar as a conforming implementation shouldn't corrupt other members. Assuming normal integer overflow semantics apply, the only problematic case is signed overflow, which may raise a signal (but I'm hard pressed to see that happening in practice if the bit field width is smaller than a full word as harware support for overflow detection will be lacking) and is otherwise implementation-defined. This caveat does not apply to your example as the target types are unsigned.
Keep in mind that bit field semantics are largely implementation-defined - even using a type other than int is actually a language extension - and it's up to you to check that the compiler does what you expect it to do on all relevant platforms.
A more portable but also less convenient approach would be to just use an uint32_t and do the bit fiddling manually. IF you don't need that protability, it should be fine as-is.
Related
I need to store the state of 8 relays in EEPROM. I didn't want to bother with shifting and I like using bitfields. So I wanted to define them like this:
typedef struct{
uint8_t RELAY0_STATE:1;
uint8_t RELAY1_STATE:1;
uint8_t RELAY2_STATE:1;
uint8_t RELAY3_STATE:1;
uint8_t RELAY4_STATE:1;
uint8_t RELAY5_STATE:1;
uint8_t RELAY6_STATE:1;
uint8_t RELAY7_STATE:1;
}relay_nvm_t;
relay_nvm_t relay_nvm;
In my main code flow, I set the state of each relay using relay_nvm variable. Example
...
if(something)
{
relay_nvm.RELAY0_STATE = 1;
relay_nvm.RELAY1_STATE = 0;
relay_nvm.RELAY2_STATE = 1;
relay_nvm.RELAY3_STATE = 0;
relay_nvm.RELAY4_STATE = 1;
relay_nvm.RELAY5_STATE = 1;
relay_nvm.RELAY6_STATE = 0;
relay_nvm.RELAY7_STATE = 1;
}
Then finally when I need to read/write to EEPROM, I just cast relay_nvm to uint8_t to read/write one byte to EEPROM. But I get error: conversion to non-scalar type requested error. Below are my functions.
static void NVM_Relay_Read(void)
{
relay_nvm = (relay_nvm_t)NVM_ReadEepromByte(NVM_RELAY_INDEX);
}
static void NVM_Relay_Write(relay_nvm_t rs)
{
NVM_WriteEepromByte(NVM_RELAY_INDEX, (uint8_t)rs);
}
Is there any way we can get over this error? I thought I could just do it by typecasting. The use of bitfields makes my job very easy and makes the code easy to understand.
I know that bitfields may not be safe in this case due to the padding but I think I can get over it using POP-PUSH (is it worth?)
I see more ways to handle this:
Use union.
Use pointer type-cast: *((uint8_t*)&relay_nvm)
Use uint8_t:
uint8_t relay_nvm;
#define RELAY0_MASK 1
#define RELAY1_MASK 2
#define RELAY2_MASK 4
...
#define RELAY7_MASK 128
// set exact relays state:
relay_nvm = RELAY0_MASK | RELAY2_MASK | RELAY4_MASK | ... ;
// set single relay (others left unchanged):
relay_nvm |= RELAY2_MASK;
// clear single relay (others left unchanged):
relay_nvm &= ~RELAY2_MASK;
// check current state of a relay:
if (relay_nvm & RELAY2_MASK) { ... }
I didn't want to bother with shifting and I like using bitfields.
If using the bitwise operators is a "bother", then you probably shouldn't be writing embedded systems code until you've gotten a hang of them... this is a very poor rationale for writing non-standard, non-portable code.
Unlike the bitwise version, bit-fields come with tons of problems: undefined bit order, endianess-dependence, poorly specified signedness, alignment & padding hiccups and so on. You are already writing platform-specific code with a uint8_t bit-fields, because those are not covered by the C standard.
If you insist on using bit-fields, you have to read the specific compiler docs about how they are implemented. Don't assume there are any guarantees regarding how things are allocated, because there is no standardization in this case.
Your specific problem is that you can't convert directly from a struct type (an aggregate - in plain english a "container type") to a uint8_t and back, for the same reason as why you can't do that with an array. You will have to use a pointer to the first element instead, then cast that one to uint8_t* and de-reference. But that comes with a bunch of other problems related to alignment, compatible types and "strict aliasing".
Structs and unions in general aren't very suitable for memory-mapping purposes, especially not when it comes to portability. At a minimum, you have to enable packing with #pragma pack(1) or similar compiler-specific commands.
So you should really consider dropping the bit-field entirely and use bitwise operators on a raw uint8_t, since you get rid of so many problems that way.
As a side-note, all variables stored in EEPROM must be volatile qualified, as must all pointers to them. Otherwise there is a big chance that the program goes haywire when you enable optimizations.
I have a C struct with 8 boolean variables that I want to be packed in 1 byte.
This how I define the struct:
typedef struct
{
bool a : 1;
bool b : 1;
bool c : 1;
bool d : 1;
bool e : 1;
bool f : 1;
bool g : 1;
bool h : 1;
} test_struct;
Then I would like to be sure it's OK to set values:
test_struct my_struct = { 0 };
my_struct.e = true;
...and do test like this:
if (my_struct.e)
// do something
Will all compilers provide the expected result, i.e. use one bit to store the bool values?
Using a bool bitfield for the sake of storing a number of flags at unknown places in memory is actually about the only safe and portable use of bit-fields.
Will all compilers provide the expected result, i.e. use one bit to store the bool values?
Yes. bool/_Bool is a standardized type for bit-fields and the code you have posted is guaranteed to behave the same across compilers.
However, massive problems arrive as soon as you start to assume allocation order. For example *(uint8_t*)&my_struct could literally result in anything. You can't portably assume where the individual booleans end up in memory.
A safer, cleaner and more portable way is to never use bit-fields, but to use bit-wise operators:
#define a (1u << 0)
#define b (1u << 1)
#define c (1u << 2)
...
uint8_t test = 0;
Then:
Set a single bit: test |= a
Clear a single bit: test &= ~a.
Check if bit set test & a.
For larger types, this is even fully portable across different CPU endianess.
Per C 2018 6.7.2.1 5, a bit-field may have type _Bool:
A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type…
Presuming the source includes <stdbool.h>, bool is _Bool, so the structure definition is okay.
Note that this:
test_struct my_struct = { 0 };
initializes all named members of the structure to zero. This is a result of the specification of initializers; it does not have anything to do with whether the bit-fields are packed into a single byte or not.
Will all compilers provide the expected result, i.e. use one bit to store the bool values?
Yes, the C standard requires C implementations to pack bit-fields as long as they fit in the addressable storage unit the implementation chooses to use for bit-fields, in C 6.7.2.1 11:
… If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit…
So, if you have consecutive one-bit bit-fields that fit within a byte (which must be at least eight bits), and are not preceded by other bit-fields, they must be packed into one byte.
The individual struct members here all refer to a single bit, and most compilers will pack these together. However, they are not required to; Nor is there a requirement for them to make the total size of the struct 8 bits; although again you can generally expect this to be the case on most platform/compiler combinations you're likely to care about. Code that relies on this is going to be fairly portable, but if you're looking for an absolute guarantee you won't find it in the C standard.
Setting:
I define an enum in C99:
enum MY_ENUM {TEST_ENUM_ITEM1, TEST_ENUM_ITEM2, TEST_ENUM_ITEM_MAX};
I ensure with compile time asserts that TEST_ENUM_ITEM_MAX does not exceed UINT16_MAX. I assume little endian as byte order.
I have a serialize-into-buffer function with following parameters:
PutIntoBuffer(uint8_t* src, uint32_t count);
I serialize a variable holding an value into a buffer. For this task i access the variable, holding the enum, like this:
enum MY_ENUM testVar = TEST_ENUM_ITEM;
PutIntoBuffer((uint8_t*) &testVar, sizeof(uint16_t));
Question: Is it legitimate to access the enum (which is an int) in this way? Does C standard guarantee the intended behaviour?
It is legitimate as in "it will work if int is 16 bits". It does not violate any pointer aliasing rules either, as long as you use a character type like uint8_t. (De-serializing is another story though.)
However, the code is not portable. In case int is 32 bit, the enumeration constants will turn 32 bit too, as may the enum variable itself. Then the code will turn endianess-dependent and you might end up reading garbage. Checking TEST_ENUM_ITEM_MAX against UINT16_MAX doesn't solve this.
The proper way to serialize an enum is to use a pre-generated read-only look-up table which is guaranteed to be 8 bits, like this:
#include <stdint.h>
enum MY_ENUM {TEST_ENUM_ITEM1, TEST_ENUM_ITEM2, TEST_ENUM_ITEM_MAX};
static const uint8_t MY_ENUM8 [] =
{
[TEST_ENUM_ITEM1] = TEST_ENUM_ITEM1,
[TEST_ENUM_ITEM2] = TEST_ENUM_ITEM2,
};
int main (void)
{
_Static_assert(sizeof(MY_ENUM8)==TEST_ENUM_ITEM_MAX, "Something went wrong");
}
The designated initializer syntax improves the integrity of the data, should the enum be updated during maintenance. Similarly, the static assert will ensure that the list contains the right number of items.
I saw few questions here about how to force an enum to 8 or 16 bits. The common answer was that it can be done in C++11 or higher. Unfortunately, I don't have that luxury.
So, here is what I'm thinking. I only need the enum to be 8 bits when it's in a struct whose size I want to minimize. So:
Option A:
typedef enum { A, B, C, MAX = 0xFF } my_enum;
struct my_compact_struct
{
my_enum field1 : 8; // Forcing field to be 8 bits
uint8_t something;
uint16_t something_else;
};
I think most or all optimizers should be smart enough to handle the 8 bitfield efficiently.
Option B:
Not use an enum. Use typedef and constants instead.
typedef uint8_t my_type;
static const my_type A = 0;
static const my_type B = 1;
static const my_type C = 2;
struct my_compact_struct
{
my_type field1;
uint8_t something;
uint16_t something_else;
};
Option A is currently implemented and seems to be working, but since I want to do (now and in the future) what's correct now and not just what's working, I was wondering if option B is clearly better.
Thanks,
If your specific values in an enum can fit into a smaller type than an int, then a C implementation is free to choose the underlying type of the enum to be a smaller type than an int (but the type of the enum constants in this case will be int). But there is no way you can force a C compiler to use a type smaller than an int. So with this in mind and the fact that an int is at least 16 bits, you're out of luck.
But enums in C are little more than debugging aids. Just use an uint8_t type if you compiler has it:
static const uint8_t something = /*some value*/
If not then use a char and hope that CHAR_BIT is 8.
Option B would be best. You'd be defining the type in question to be a known size, and the const values you define will also be the correct size.
While you would lose out on the implicit numbering of an enum, the explicit sizing of the field and its values makes up for it.
Step into same problem, I solved it this by using attribute((packed)). Packet align data to 4 Bytes, with packet (int)sizeof(my_compact_struct) = 4, without packet (int)sizeof(my_compact_struct) = 8
typedef enum __attribute__((packed)){
A = 0x01
B = 0x10
C = 0x255 // Max value, or any other lower than this
} my_enum;
struct my_compact_struct __attribute__((packed)){
my_enum field1 : 8; // Forcing field to be 8 bits
uint8_t something;
uint16_t something_else;
};
You can use enum or even typedef enum like a grouped #define. Do not actually define a structure member, data stream, or global variable as type enum. Rather define all storage members as fixed types like uint8_t. As if you used #define just set them to the enum literal constants. If you use any kind of lint tool, then this design style will raise some messages which you will need to tailor. Just like malformed #define if the literal value doesn't fit, then bad things can happen, and either way you need to pay attention. In a debugger or hardware simulator, the enum can provide useful display reference information. Temporary variables are an exception to how global definitions are treated. For function parameters or automatic variables, and only then, define them with the enum type. In this context int is going to be the most efficient word size as well as the standard behavior of enum. There is no error possible nor hyper-optimizing you can do.
I am getting error
error: aggregate value used where an integer was expected
on compiling this code:
#include <stdio.h>
typedef unsigned long U32;
typedef struct hello_s
{
U32 a:8;
U32 b:24;
}hello_t;
int main()
{
hello_t str;
U32 var;
str.a = 0xAA;
str.b = 0xAAA;
var = (U32)str;
printf("var : %lX\n", var);
return 0;
}
Can someone please explain what the error means, and what I am doing wrong.
EDIT: I understand this is a stupid thing to do. What I wanted to know was why the compiler is crying about this. Why cant it just assign the first 32 bits to the integer.
var = (U32)str;
Because str is an object of a structure type and you cannot convert structure objects to object of arithmetic types. C does not let you perform this kind of conversion.
If you want to access you structure object as an integer you can create an union of your structure and of an U32.
Note that the common construct var = *(U32 *) str; is undefined behavior in C. It violates aliasing and alignment rules.
Well, I think one should not mistake the C99 spec by assuming that it is against the language standards.
The standard only says that the results of the conversion may not portable across different machines/architectures.
As long as you have coded the program to work on a particular architecture, it's fine.
For example I group selective members of a data structure DataStr_t that uniquely identifies the object (i.e., the key), packed into another struct, say, DataStrKey_t. I'll make sure that sizeof(DataStrKey_t) is equal to sizeof(uint64) and for all practical purposes use it as uint64 as it is easy to handle.
I also do the below operation often:
memcmp(&var_uint64, &var_DataStructKey, sizeof(uint64));
If you read access or write access the object using the key on a machine the value resulting from conversion is predictable and consistent in bit-wise alignment.
Well if you move "only this data" to a different machine (which actually didn't write the data) and try to read it, things may break.
Your program slightly modified for more explanation and successful compilation:
Here, as long as LINE_A and LINE_B are execute on the same machine result is always predictable.
But if you write the (var_uint64,var_DataStructKey) to a file and read it from a different machine, then execute LINE_B on those populated values, comparison "may" fail.
#include <stdio.h>
#include <string.h>
typedef unsigned long U32;
typedef struct hello_s
{
U32 a:8;
U32 b:24;
}hello_t;
int main()
{
hello_t str;
U32 var;
str.a = 0xAA;
str.b = 0xAAA;
var = *(U32 *)(&str); //LINE_A
if(0 == memcmp(&var, &str, sizeof(U32))) //LINE_B
printf("var : %lu\n", var);
return 0;
}
I guess my answer is too late, but attempted to explain.
And what do you expect that cast to result in exactly? You could always just cast it's address to a pointer to int and dereference it... but are you sure you can safely do so (no, you can't)? Is structure member alignment going to bite you someday (the answer is "probably, yes, it depends")?
Also, from the C99 styandard:
C99 §6.7.2.1, paragraph 10: "The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined."
That is not always a stupid thing to do. In my case, I have a struct that I need to send over a network connection. The data must be sent over an SPI bus in byte form, so the struct must be accessed a byte at a time. I used this define to access each byte. You must be aware of the byte ordering of your platform to do this correctly. Also, you must make sure your structs are __PACKED (see also: C/C++: Force Bit Field Order and Alignment), so the compiler does not insert any padding blocks or alignment blocks. This will also not work if any of the bit members fall across the byte boundaries (at least, with the Microchip XC16 compiler it does not).
typedef unsigned char byte;
#define STRUCT_LB(x) ((byte *)(&x))[0]
#define STRUCT_HB(x) ((byte *)(&x))[1]
A nicer way to do this is to define your struct as a union of a bitfield and a byte array like so:
typedef unsigned char byte;
typedef struct {
union {
struct __PACKED {
byte array[2];
} bytes;
struct __PACKED {
byte b0: 1;
byte b1: 1;
byte b2: 1;
byte b3: 1;
byte b4: 1;
byte other: 3;
byte more: 6;
byte stuff: 2;
} fields;
};
} MyData;
Not all typecasting are allowed in C. Per this manual, only the following cases are legal,
Convert an integer to any pointer type.
Convert a pointer to any integer type.
Convert a pointer to an object to a pointer to another object.
Convert a pointer to a function to a pointer to another function.
Correctness of converting null between pointers (either object or function).
Hence, casting a struct to an integer is obviously not a legal conversion.