问题
I am trying to get rid of Rule 11.3 from my code.
Sample code:
static int32_t
do_test(const char *cp)
{
const char *c = cp;
const int32_t *x;
x = (const int32_t *)cp;
return *x;
}
I want the value of *c and *x to be same. Even-though the code is compiling and giving the correct value, "x = (int32_t *)cp;" causing violation of 11.3 and 11.8
Rule 11.3 violation: An object with pointer type shall not be converted to a pointer to a different object type.
I have tried with void pointer, but the result was not same as what I expected and also it resulted in additional violation.
Is there anyway to remove these violations ?
From MISRA C 2012 Document they are mentioning like there is an exception for this rule as it is permitted to convert a pointer to object type into a pointer to one of the object types char, signed char or unsigned char. The Standard guarantees that pointers to these types can be used to access the individual bytes of an object.
Ignore Dir 4.6 due to char type.
回答1:
You are lucky you are using MISRA-C, because this code is full of bugs. You cannot make the bugs go away with a cast.
Bug 1. The character pointer is not necessarily aligned, in which case your code invokes undefined behavior as per the C standard 6.3.2.3/7:
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.
Bug 2. The code contains a blatant strict aliasing violation. This is always undefined behavior as per the C standard 6.5/7.
Your assumption "The Standard guarantees that pointers to these types can be used to access the individual bytes of an object." is correct: as a special exception C allows you to convert from a pointer-to-x to pointer to char and then access the data through the char poiner. But not the other way around.
Your code is not accessing individual bytes; you are going the other way around, from an array of characters to a 32 bit type. This is not allowed. See What is the strict aliasing rule?.
Correct code, that should be ok with both the C language and MISRA-C:
static int32_t do_test(const char *cp)
{
return (int32_t) ((uint32_t)cp[0] << 24u) |
((uint32_t)cp[1] << 16u) |
((uint32_t)cp[2] << 8u) |
((uint32_t)cp[3]);
}
This shift version is always preferred, as it is endianess independent and therefore portable. The casts to uint32_t
are necessary to prevent implicit promotions on 8/16 bit systems, plus you should never do bit shift on signed types.
回答2:
If you feel a need to avoid the explicit cast, you can always do memcpy
:
#include <string.h>
#include <stdint.h>
static int32_t
do_test(const char *cp)
{
int32_t r;
memcpy(&r,cp,sizeof(r));
return r;
}
With an optimizing compiler that has a builtin mempcy
, this should be just as efficient as return *(int32_t*)cp;
(your code, written more succinctly).
Keep in mind that in either case, the code is only defined if the cp
value you passed in points to a valid int32_t
object.
If memcpy isn't OK because of the implicit char*
to void*
cast, you could replace it with a custom-made trivially implemented void charwise_memcpy(char *Dest, char const *Src, size_t Sz);
or the equivalent for
loop.
void charwise_memcpy(char *Dest, char const *Src, size_t Sz)
{
for(size_t i=0; i<Sz; i++)
Dest[i]=Src[i];
}
回答3:
The original code may cause undefined behaviour:
const char *cp;
// ...
x = (const int32_t *)cp;
If int32_t
has an alignment requirement on the platform, and cp
is not correctly aligned for that requirement, the behaviour is undefined.
I'm not a fan of MISRA in general but this particular instance seems well justified. Even if you happen to be on a platform with no alignment requirements (unlikely even in embedded development), this code is non-portable and might start breaking if you move to a CPU that does have alignment requirements.
A good solution is to use memcpy
instead which is well-defined:
来源:https://stackoverflow.com/questions/47617615/misra-c-2012-rule-11-3-violation-while-trying-to-do-a-typecast-from-char-to-int