RT-Thread之rt_memset函数

在rtthread中,自带了内存设置函数rt_memset,其目的是将某段内存的内容设置为指定的数值。该函数接受三个参数:源内存地址s,要设置的值c,以及要设置的长度count。在函数中,首先判断了是否使用了RT_USING_TINY_SIZE宏,如果使用了,则通过循环逐个设置内存的值,然后返回源内存地址。

如果没有使用RT_USING_TINY_SIZE宏,则会根据内存对齐情况和设置的值大小进行不同的处理。如果设置的长度大于等于一个块的大小并且内存地址是块对齐的,就会使用较高效的方式进行设置,否则会通过逐个字节设置的方式来完成设置操作。

下面是这个函数的具体内容:

/**
 * This function will set the content of memory to specified value
 *
 * @param s the address of source memory
 * @param c the value shall be set in content
 * @param count the copied length
 *
 * @return the address of source memory
 */
void *rt_memset(void *s, int c, rt_ubase_t count)
{
#ifdef RT_USING_TINY_SIZE
    char *xs = (char *)s;

    while (count--)
        *xs++ = c;

    return s;
#else
#define LBLOCKSIZE      (sizeof(rt_int32_t))
#define UNALIGNED(X)    ((rt_int32_t)X & (LBLOCKSIZE - 1))
#define TOO_SMALL(LEN)  ((LEN) < LBLOCKSIZE)

    int i;
    char *m = (char *)s;
    rt_uint32_t buffer;
    rt_uint32_t *aligned_addr;
    rt_uint32_t d = c & 0xff;

    if (!TOO_SMALL(count) && !UNALIGNED(s))
    {
        /* If we get this far, we know that n is large and m is word-aligned. */
        aligned_addr = (rt_uint32_t *)s;

        /* Store D into each char sized location in BUFFER so that
         * we can set large blocks quickly.
         */
        if (LBLOCKSIZE == 4)
        {
            buffer = (d << 8) | d;
            buffer |= (buffer << 16);
        }
        else
        {
            buffer = 0;
            for (i = 0; i < LBLOCKSIZE; i ++)
                buffer = (buffer << 8) | d;
        }

        while (count >= LBLOCKSIZE * 4)
        {
            *aligned_addr++ = buffer;
            *aligned_addr++ = buffer;
            *aligned_addr++ = buffer;
            *aligned_addr++ = buffer;
            count -= 4 * LBLOCKSIZE;
        }

        while (count >= LBLOCKSIZE)
        {
            *aligned_addr++ = buffer;
            count -= LBLOCKSIZE;
        }

        /* Pick up the remainder with a bytewise loop. */
        m = (char *)aligned_addr;
    }

    while (count--)
    {
        *m++ = (char)d;
    }

    return s;

#undef LBLOCKSIZE
#undef UNALIGNED
#undef TOO_SMALL
#endif
}

在这个函数中,先判断有没有使用RT_USING_TINY_SIZE宏。如果使用了这个宏,那么就按着一个一个的字节来进行内存的内容设置。

如果使用了这个宏,先进行了三个宏定义:

#define LBLOCKSIZE      (sizeof(rt_int32_t))                     
#define UNALIGNED(X)    ((rt_int32_t)X & (LBLOCKSIZE - 1))      
#define TOO_SMALL(LEN)  ((LEN) < LBLOCKSIZE)

第一个宏定义是用于确定块的大小,为什么要使用 LBLOCKSIZE

  1. 提高效率 :处理内存时,如果内存是对齐的,并且可以一次操作多个字节,那么操作的效率会更高。通过一个操作一个块(例如一个块有四个字节)的方式,可以减少循环次数,从而提高性能。
  2. 内存对齐 :某些硬件平台对未对齐的内存访问会有性能损失,甚至可能导致硬件异常。使用对齐的块大小可以避免这些问题。

第二个宏定义用于检查地址X是否对齐,如果没有对齐,那么结果就是非零也就是真。

第三个宏定义用来判断长度LEN是不是小于块的大小,如果小于那么结果就是真。

接着定义了一个用于for循环的变量i,一个游标指针,一个4字节的缓存变量buffer,一个对齐的地址aligned_addr,最后还定义了一个变量d,用于取下参数c的低八位(因为这里是要对每一个字节,也就是八个比特进行内存设置)。

然后我们进行了一个if判断,进入到这个if判断的条件是被操作的内存足够大并且已经进行了内存对齐。从注释中我们也可以得知进入到if判断的条件:

/* If we get this far, we know that n is large and m is word-aligned. */

下面开始设置内存块的内容。这里分为了两种情况,如果内存对齐的长度为4个字节,经过两次(1 * 2 = 2 2 * 2 = 4)就可以得到一个内存被设置为目标值的内存块。
如果内存对齐长度不为4,只能采用for循环的方式去设置内存块的内容(1 + 1 = 2 2 + 1 =3)。

这样做的目的是为了提高某些情况下的性能,第一种方式完成长度为4的内存块内容的设置只需要进行2次操作,而在第二种方法下进行了3次。随着内存块长度的不多增长,性能差异会更加明显。

举个例子(内存对齐长度为4),如果传入的参数c的值为542 <=> 0010 0001 1110,那么经过rt_uint32_t d = c & 0xff; 这句代码的处理,d = 0001 1110,接着经过两步;
buffer = (d << 8) | d;
buffer |= (buffer << 16);

第一步buffer = 0001 1110 0000 0000 | 0001 1110 = 0001 1110 0001 1110
第二部buffer = 0001 1110 0001 1110 0001 1110 0001 1110

这样下来会得到LBLOCKSIZE长度的目标内存块。

接着的while循环开始对目标地址进行内存设置。但是仍然会有剩余的不足一个LBLOCKSIZE大小的内存没有被设置,所以就需要按字节而不是按块进行内存设置。

最后返回了一个指向内存段开头的指针并取消了开头的三个宏定义:

#undef LBLOCKSIZE
#undef UNALIGNED
#undef TOO_SMALL

最后我想了一个问题,为什么这样的函数要有返回值,chatgpt给出的解释是这样的:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值