MISRA.UNIONUnion is used. MISRA C 2012 Rule 19.2 The union keyword should not be usedC90 [Undefined 39, 40; Implementation 27], C99 [Unspecified 10; Undefined 61, 62] Category: Advisory Analysis: Decidable, Single Translation Unit Applies to: C90, C99 RationaleA union member can be written and the same member can then be read back in a well-defined manner. However, if a union member is written and then a different union member is read back, the behaviour depends on the relative sizes of the members:
The Standard permits the bytes of a union member to be accessed by means of another member whose type is array of unsigned char. However, since it is possible to access bytes with unspecified values, unions should not be used. If this rule is not followed, the kinds of behaviour that need to be determined are:
ExampleIn this non-compliant example, a 16-bit value is stored into a union but a 32-bit value is read back resulting in an unspecified value being returned. uint32_t zext ( uint16_t s ) { union { uint32_t ul; uint16_t us; } tmp; tmp.us = s; return tmp.ul; /* unspecified value */ } See alsoRule 19.1 MISRA-C 2004 Rule 18.4 (required): Unions shall not be used.Union is used. [Implementation 27] Rule 18.3 prohibits the reuse of memory areas for unrelated purposes. However, even when memory is being reused for related purposes, there is still a risk that the data may be misinterpreted. Therefore, this rule prohibits the use of unions for any purpose. It is recognised nonetheless that there are situations in which the careful use of unions is desirable in constructing an efficient implementation. In such situations, deviations to this rule are considered acceptable provided that all relevant implementation-defined behaviour is documented. This might be achieved in practice by referencing the implementation section of the compiler manuals from the design documentation. The kinds of implementation behaviour that might be relevant are:
The use of deviations is acceptable for (a) packing and unpacking of data, for example when sending and receiving messages, and (b) implementing variant records provided that the variants are differentiated by a common field. Variant records without a differentiator are not considered suitable for use in any situation. Packing and unpacking dataIn this example, a union is used to access the bytes of a 32-bit word in order to store bytes received over a network most-significant byte first. The assumptions that this particular implementation rely on are:
The code to implement the receipt and packing of the bytes could be: typedef union { uint32_t word; uint8_t bytes[4]; } word_msg_t; uint32_t read_word_big_endian (void) { word_msg_t tmp; tmp.bytes[0] = read_byte(); tmp.bytes[1] = read_byte(); tmp.bytes[2] = read_byte(); tmp.bytes[3] = read_byte(); return (tmp.word); } It is worth noting that the body of the routine could be written in a portable manner as follows: uint32_t read_word_big_endian (void) { uint32_t word; word = ((uint32_t)read_byte()) << 24; word = word | (((uint32_t)read_byte()) << 16); word = word | (((uint32_t)read_byte()) << 8); word = word | ((uint32_t)read_byte()); return (word); } Unfortunately, most compilers produce far less efficient code when faced with the portable implementation. When high execution speed or low program memory usage is more important than portability, the implementation using unions might be preferred. Variant recordsUnions are often used to implement variant records. Each variant shares common fields and has additional fields that are specific to the variant. This example is based on the CAN Calibration Protocol (CCP), in which each CAN message sent to a CCP client shares two common fields, each of one byte. Up to 6 additional bytes may follow, the interpretation of which depends on the message type stored in the first byte. The assumptions that this particular implementation rely on are:
In the interests of brevity, only two message types are considered in this example. The code that is presented here is incomplete and should be viewed merely to illustrate the purpose of variant records and not as a model implementation of CCP. /* The fields common to all CCP messages */ typedef struct { uint8_t msg_type; uint8_t sequence_no; } ccp_common_t; /* CCP connect message */ typedef struct { ccp_common_t common_part; uint16_t station_to_connect; } ccp_connect_t; /* CCP disconnect message */ typedef struct { ccp_common_t common_part; uint8_t disconnect_command; uint8_t pad; uint16_t station_to_disconnect; } ccp_disconnect_t; /* The variant */ typedef union { ccp_common_t common; ccp_connect_t connect; ccp_disconnect_t disconnect; } ccp_message_t; void process_ccp_message (ccp_message_t *msg) { switch (msg->common.msg_type) { case Ccp_connect: if (MY_STATION == msg->connect.station_to_connect) { ccp_connect (); } break; case Ccp_disconnect: if (MY_STATION == msg->disconnect.station_to_disconnect) { if (PERM_DISCONNECT == msg->disconnect.disconnect_command) { ccp_disconnect (); } } break; default: break; /* ignore unknown commands */ } } MISRA-C++ 2008 Rule 9—5—1 (required): Unions shall not be usedThis rule is also covered by MISRA.ASSIGN.OVERLAP. [Implementation 3.9(4, 5)] RationaleThe use of unions to access an object in different ways may result in the data being misinterpreted. Therefore, this rule prohibits the use of unions for any purpose. It is recognized nonetheless that there are situations in which the careful use of unions is desirable in constructing an efficient implementation. In such situations, deviations to this rule are considered acceptable provided that all relevant implementation-defined behaviour is documented. This might be achieved in practice by referencing the implementation section of the compiler manuals from the design documentation. Examplenamespace NS1 { // Compliant - no union } namespace NS2 { union U1 // Non-compliant — union { int32_t i; float32_t j; }; } |