0

"Don't!" is the correct answer, but unfortunately it's not the one I need.

If I do:

size_t array_size = func_that_calc_array_size();
char *foo = new char[array_size];
if (array_size > 42)
    foo[42] = 'X';

This is all perfectly legal, but my MISRA C++ code checker gives an error 5-0-15 on the foo[42], which says that "Array indexing shall be the only form of pointer arithmetic". This question has actually been asked before, but the question and answer missed a critical issue, namely that the documentation further states:

Array indexing shall only be applied to objects defined as an array type.

If you look at the documentation (a suspiciously bootleg copy can be found by searching for "misra c++ 2008 pdf") it has an example similar to:

void my_fn(uint8_t *p1, uint8_t p2[])
{
    p1[5] = 0; // Non-compliant - p1 was not declared as array
    p2[5] = 0; // Compliant
}

So, basically the code-checking tool matches the declaration to the usage. Is there any possible way to convert the pointer to an array?


In our real example, we are using OpenCV's uchar *cv::Mat::ptr(), so we can't just reserve a large-enough array.

Ken Y-N
  • 14,644
  • 21
  • 71
  • 114
  • Would `*(foo+42)='X';` pass this (rather silly) rule? – Raymond Chen Jun 03 '19 at 02:20
  • I wish they'd just come up with MISRA-C and MISRA-C++, which are strict subsets of C and C++ that disallow all the forbidden constructs. – Eljay Jun 03 '19 at 02:20
  • @RaymondChen Nope, that is non-compliant too – Ken Y-N Jun 03 '19 at 02:22
  • And you are allowed to use `new`? – Amadeus Jun 03 '19 at 02:40
  • I know this isn't the answer you're looking for, but it needs to be said. The rules are there for a reason: Working around them is not only counter-productive, it's just silly. The MISRA coding standards are **very** strict, and that is because they are used for safety-critical systems: If you're writing one of those, then you shouldn't really be trying to circumvent any rules enforced by your coding standards: Instead, you should be designing your code to never need to do so. – Not a real meerkat Jun 03 '19 at 02:42
  • In your example, merely declaring `foo` as a `char[]` would already suffice: I know your real world problem is not as simple, so what I'm saying is: If `foo` needs to be a `char[]`, then make it a `char[]`, even if that implies having to do a lot of work to fix your codebase to comply with the new definition. – Not a real meerkat Jun 03 '19 at 02:45
  • 1
    @KenY-N How about `std::advance(foo, 42) = 'X'`, does it fail as well? I have fixed similar issue reported by clang-tidy with this trick. – R2RT Jun 03 '19 at 02:58
  • 1
    `char *foo = new foo[array_size];` That probably won't pass compilation, not even speaking about any MISRA madness. – n. m. could be an AI Jun 03 '19 at 03:29
  • 1
    @n.m. you seem to assume that what they mean by "an array type" is the same as what the standard means by array types. This is clearly not the case. Although yes, the compiler will interpret those as the same type, they are clearly different, as they signify different intentions. They are not writing the standard for C++, they are writing a standard to be applied by humans, and you are not in any way forced to comply to it. Finally, you may disagree with them, and that's OK, but if someone is using their standard, then they should *use their standard*. That's my whole point. – Not a real meerkat Jun 03 '19 at 03:30
  • @CássioRenan Where does MISRA C define this "array type" thing? Can you quote their document? "you are not in any way forced to comply to it" Sure you can quit your day job, no one is employing you against your will right? If the intent of their guidelines is to disallow variable size arrays, there should be an explicit rule against variable size arrays that mentions them by name, so that people stop searching for workarounds. If their intent is to allow them, please show how to use tgem without violating their rules. – n. m. could be an AI Jun 03 '19 at 03:43
  • @n.m. Oops, typo. It should have been a `new char[]` of course. – Ken Y-N Jun 03 '19 at 05:43
  • @n.m. It's easy to dismiss something you don't understand. The purpose of the rule is to ban `*(arr+i)` notation over `arr[i]`, nothing else. Do you suggest that we use the former? If not, then it turns out that MISRA does know what they are talking about. The existence of MISRA is rather there because the standard committees are failing to deliver safe and stable languages. It is undeniable that C and particularly C++ are very dangerous languages if used out of the box, with no restrictions or coding standards. They will shoot you in the foot or blow your whole leg off. – Lundin Jun 03 '19 at 08:05
  • @n.m. MISRA-C doesn't "define an array type". This rule has been dropped entirely in MISRA-C:2012, but unfortunately MISRA-C++ hasn't been revised like MISRA-C was. Probably because C++ isn't very popular for these kind of systems. – Lundin Jun 03 '19 at 08:08
  • @Lundin "The purpose of the rule is to ban *(arr+i) notation over arr[i]" Why is `p1[5] = 0;` banned then? – n. m. could be an AI Jun 03 '19 at 09:23
  • @n.m. It is not. If some buggy tool whines about that, it's no fault of MISRA. They neither provide nor certify static analysers (although there's plenty of tool vendors sitting in the committee). – Lundin Jun 03 '19 at 09:27
  • @Lundin if your goal is to make safe systems, you should use a safe language, not to slap cosmetic guidelines onto an unsafe one. – n. m. could be an AI Jun 03 '19 at 09:29
  • @n.m. Wish all you want, no such language exists, which is both safe and suitable for embedded or mission-critical applications. So until one is invented, you must use a safe subset such as MISRA-C. Safety standards like the "SILs" in practice only allow C, C++ and Ada, since those are the only suitable languages that also have industry standard safe subsets. Using C++ for these kind of applications is very questionable in my personal opinion, but it is allowed. – Lundin Jun 03 '19 at 09:33
  • 1
    @n.m. The original rule _was_ problematic, since the wording was bad and the purpose wasn't clear. I can see that I protested about it too on the official MISRA-C forums some 12 years ago. The rule was entirely withdrawn in MISRA-C:2012, 8 years ago. Unfortunately MISRA-C++ has not been updated, again perhaps because C++ is getting less and less popular to use for these systems. – Lundin Jun 03 '19 at 15:08

2 Answers2

1

The validity of the rule is quite dubious. In the example, p1 and p2 have exactly the same type: They are both pointers.

If p2 truly conforms to the rule, then the solution is to introduce a function, so that you can make use of the function-argument-array-to-pointer-adjustment. Here is an example with a lambda, but you can use a regular function as well:

char *foo = new foo[array_size];
if (array_size > 42)
    [](char foo[]) {
        foo[42] = 'X';
    }(foo);

C++20 introduces std::span, which appears to be a solution to the problem:

std::span foo_span{foo, array_size};
if (array_size > 42)
    foo_span[42] = 'X';

This uses a class overload of the subscript operator, and not pointer-subscript, so it appears to conform to the rule. std::span is probably not implementable without violating MISRA, but so are many other things in the standard library, so I suspect that is not a problem.


In our real example, we are using OpenCV's uchar *cv::Mat::ptr(), so we can't just reserve a large-enough array.

Perhaps to follow the spirit of the rule, rather than the letter, you should be passing a cv::Mat& into the function rather than char*.

P.S. I suspect that OpenCV does not conform to MISRA, so depending on it is probably not the best thing to do if the program has to conform to MISRA.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • There's a lot of MISRA-C++ rules that are counter-productive... Furthermore, passing a `char *` to a function that expects a `char []` violates the same rule. I think I'll write a template pointer conversion function so I can isolate the error to a single line and document it as an exception. – Ken Y-N Jun 03 '19 at 02:29
  • @KenY-N Why nit implement a class template while you are at it? They have one in the standard library called `vector` you can model yours after. – n. m. could be an AI Jun 03 '19 at 03:26
  • At the end of the day, we do need to address the `cv::Mat` pixels (`at<>()` has horrible performance). OpenCV is an acceptable library for this project despite its non-MISRAness. – Ken Y-N Jun 03 '19 at 05:41
  • You can't use anything but C++03 for MISRA-C++, so this doesn't answer the question. Using new features from C++11 and beyond is a big no-no. – Lundin Jun 03 '19 at 07:44
  • @Lundin I'm not sure of the exact reason, but we are allowed C++11 for this project, if we can justify it, such as `for(auto const &elem: array){}` We are using *Company A*'s rule book that permits memory allocation, for instance. – Ken Y-N Jun 03 '19 at 08:24
  • @KenY-N C++11 `auto` is one of the most dangerous features of the language, so why would you ever want to allow that? Accidentally declaring the wrong type because of some subtle implicit promotion in the initializer is disastrous program behavior. Particularly if you have function/operator overloading working on various different types, so that you end up calling the wrong function by accident too. – Lundin Jun 03 '19 at 08:32
  • @Lundin it is much much easier to declare the wrong type *without* `auto`. – n. m. could be an AI Jun 03 '19 at 09:56
  • @n.m. Because you say so or because of an actual argument? It's easy to demonstrate how blatantly dangerous it is: `auto u8a = u8b + u8c;` or `auto foo = ~u8; if(foo >= max) ...else { array[foo] = ... } // writes to negative array index`. And numerous other chances to write subtle but severe bugs. – Lundin Jun 03 '19 at 10:41
  • @Lundin I'm not really sure what your examples demonstrate, so I'd better stop talking today. – n. m. could be an AI Jun 03 '19 at 11:14
  • @n.m. Nobody is certain, probably, because `auto` made the code an unreadable mess. You spot the bug easily if you type it out explicitly: `int foo = ~u8;`. – Lundin Jun 03 '19 at 14:47
1

I think the root of the problem here is char *foo = new char[array_size];. A MISRA checker is arguably allowed to assume that this is not an array, because all dynamic memory allocation is banned.

You could try to see if you get the same error when writing char array[10]={0}; char* foo = array; because then you can dismiss this as a false positive tool bug.

The purpose and rationale of the rule is to ban the form *(x + i) instead of x[i]. Nothing else. The rule does not block the use of the [] on a pointer operand.

Several MISRA rules are however in place to ensure that all pointer arithmetic is done with operands that point at the same array, to prevent undefined behavior.

MISRA-C:2004 and MISRA-C++:2008 also had some weird, vague requirement that function parameters should be declared as char param[] rather than char* param, but since that was nonsensical, all this talk about array style indexing was removed in MISRA-C:2012.

(In fact there's no such thing as "array style indexing" in C or C++, see Do pointers support "array style indexing"?)

Lundin
  • 195,001
  • 40
  • 254
  • 396