如何编写一个完全符合MISRA:2012标准的memcpy函数?

3

我写了这个memcpy函数,但我仍然需要禁用规则11.5和11.8。是否有解决方案可以完全符合MISRA:2012标准?

#pragma cstat_suppress="MISRAC2012-Rule-21.6" // Uses of stdio.h were found.
#include <stdio.h>

#include <stdint.h>
#include <string.h>
#include <stdlib.h>

extern int main(void);

static int_least8_t _memcpy(void *dst, const void *src, const size_t length)
{
    #pragma cstat_disable="MISRAC2012-Rule-11.5" // A conversion from a pointer to void into a pointer to object was found.
    int_least8_t* destination = (int_least8_t*)dst;
    #pragma cstat_disable="MISRAC2012-Rule-11.8" // A cast that removes a const or volatile qualification was found.
    const int_least8_t* source = (int_least8_t*)src;
    #pragma cstat_restore="MISRAC2012-Rule-11.5","MISRAC2012-Rule-11.8"

    for (size_t i = 0; i < (length / sizeof(int_least8_t)); i++)
    {
        destination[i] = source[i];
    }
    return 0;
}

int main(void)
{
    int32_t src[32];
    int32_t dst[32];

    (void)memset(src, 0xff, sizeof(src));

    (void)_memcpy(dst, src, 128);

    for (size_t i = 0; i < (sizeof(src) / sizeof(src[0])); i++)
    {
        (void)printf("%d <=> %d\n", src[i], dst[i]);
    }

    return 0;
}

我正在使用IAR作为编译器,使用C-STAT进行静态分析。


2
你能给我们解释一下规则11.5和11.8吗,这样我们就不用去查找了吗?如果你把这个问题表述为一个编程问题,你会得到更多的帮助。 - Joe
@Joe完成了,但最新的MISRA修正案中有一些有趣的内容:https://www.misra.org.uk/LinkClick.aspx?fileticket=V2wsZxtVGkE%3D&tabid=57。看起来memcpy是一个特殊情况。 - nowox
2
@nowox 这个修正并不是特别有趣,似乎有人又在深挖严格别名规则了。memcpy 没有类型安全性,这就是它的本质。此外,memcpy 本身也参与了确定对象有效类型的过程。这又是一个缺乏合理解释的规则。无论如何,你的代码并不是标准库中的 memcpy,所以这个规则并不适用。 - Lundin
2
以下划线开头的名称是保留给实现的。它们不能被应用程序代码使用。顺便说一下:为什么不使用标准库的memcpy,它很可能已经高度优化,而不是使用一些自制的、速度较慢的版本? - too honest for this site
为什么你的 _memcpy 返回 0?这会让任何期望它像标准的 memcpy() 一样工作的人感到惊讶。而且完全没有必要这样强制转换掉 constconst int_least8_t* source = (const int_least8_t*)src; 应该没问题。 - Toby Speight
显示剩余4条评论
1个回答

7

您无法使用标准格式编写memcpy函数并完全符合MISRA规范。正如您所注意到的,MISRA不允许使用restrict关键字。然而,还有11.5号规则。

11.5号规则涉及从指向void的指针向指向类型的指针进行强制转换,实践中遵循这个规则过于繁琐。这是一个建议性规则,所以可以跳过它。您不需要提出偏差。

11.8号规则关于消除限定符的强制类型转换是正确的(且必需的)。在这种情况下没有理由这样做。您的代码中存在一个错误,但被MISRA防止了。请将代码更改为:

const int_least8_t* source = (const int_least8_t*) src;

附加说明:

  • main() 函数不需要提供函数声明。
  • MISRA-C 不允许使用 stdio.h
  • 避免声明以下划线开头的标识符,参见 C11 7.1.3。
  • 在此处使用 int_least8_t 没有明显的好处。此外,带符号的类型存在问题。我会使用 uint8_t 替代它。

2
@Persixty MISRA-C鼓励使用stdint.h,如果您不使用stdint类型,则需要一个好的理由。这不是一个好的理由-如果您有一个不支持uint8_t但具有16位字符的系统,则您有一些功能失调的DSP。您应该在汇编语言中编写代码,而不是在C中编写代码。对于普通的C程序来说,绝对没有必要提供对奇怪的、外来的、过时的DSP的可移植性。那些这样做的人只是浪费大家的时间。提供对真实世界的主流计算机体系结构的可移植性就足够了。 - Lundin
我之前不知道这个。我同意在汽车工业中有人使用非8位字节是非常理论的,但我认为雷诺或菲亚特可能会这样做。面对这个(坦白说非常严苛的)规则,我同意没有充分的理由。但是对齐点仍然存在。将void *强制转换为对齐类型还有另一个问题,你没有提到。 - Persixty
@Persixty 将任何 uint8_t(或字符类型)复制到另一个相同类型的变量中是完全可以的。当您尝试将指向的数据作为其他类型访问时,就会出现对齐问题,在这种情况下,您还会遇到严格别名问题 - 这是MISRA不允许的。无论如何,不对齐不是“my_memcpy”本身的问题,而是来自调用者的问题。除了基于对齐字大小的复制在某些系统上会比逐字节更快之外,没有其他问题。 - Lundin
创造一个类似于 "my_memcpy" 的函数外,发明一个像 void fastcpy (uint_fast8_t* dst, const uint_fast8_t* src, size_t size) 这样的东西也许是个不错的主意。 - Lundin
1
uint_fast8_t 可能需要考虑对齐问题。uint_fast8_t 是至少有 8 位的最快类型。在优化的平台上可能更宽并且对齐。由于被宣传为快速,所以很可能使用对齐指令!您可以编写 my_memcpy() 函数,将第一个奇数字节作为 uint8_t 处理,将主体作为更宽的类型处理,最后几个字节再作为 uint8_t 处理。 - Persixty
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接