1
votes

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.

3
Well you can fix 11.8 simply by making it a cast to const int32_t *.Oliver Charlesworth
Perhaps you could write as static int32_t do_test(const void *cp) { const int32_t *x = cp; return *x; }?P.P
I tried with void pointer. static int32_t do_test(const char *cp) { const char *c = cp; const int32_t *x; const void *vp = c; x =vp; return *x; } Code is performing as expected and not violating 11.3 but it is violating rule 11.5 now for "x = vp; " . Rule 11.5 violation: An pointer to void type "vp" shall not be converted to pointer to object. Anyway to get rid of this violation ?Salim
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.Salim
When you say "I want the value of *c and *x to be same" what do you want to happen if c points to something like: "\x01\x02\x03\x04"?Michael Burr

3 Answers

7
votes

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.

3
votes

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];
}
2
votes

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: