memset函数效率分析

简述

在处理数组的时候经常需要频繁地将数组初始化为0,这时候有两种选择,一种是使用for循环,一个元素一个元素地赋值;再者就是使用包含于string.h头文件的memset函数了。

原文:http://blog.csdn.net/hackbuteer1/article/details/7343189

函数原型

1
2
3
4
5
6
7
8
9
10
void* memset(void* s, int c, size_t n)
{
unsigned char* p = (unsigned char*) s;
while (n > 0)
{
*p++ = (unsigned char) c;
--n;
}
return s;
}

作用:将s所指向的某一块内存中的每个字节的内容全部设置为c指定的ASCII值, 块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作。

函数使用

memset函数通常都被当做当作万能的初始化工具,例如:

1
2
3
const int n = 10000; 
int arr[n];
memset(arr, 1, n * sizeof(int));

这样得到的arr数组一定不是全0,而是16843009,下面解释原因。
首先,变量类型的本质只是标志从某一内存地址开始读取的位数,强制转换就是改变读取位数的大小。

c的二进制表示: 00000000 00000000 00000000 00000001 (32位)

  1. c转换为unsigned char后:00000001(8位)
  2. 将指针p(unsigned char类型)的每一元素(8位)赋值为00000001,循环4n次。
  3. memset结束后,arr的每个元素按照int类型读取,读出来的就是00000001 00000001 00000001 00000001,十进制就是16843009。

不过如果是memset(arr, 0, n * sizeof(int))的话可以使用,因为32位都是0。memset(arr, -1, n * sizeof(int))也可以将arr数组成功赋值为-1,因为计算机在存储负数的时候采用的是补码表示,-1的补码为:11111111 11111111 11111111 11111111,因此可以成功地赋值。

效率测试

再来说memset的效率问题。使用memset函数与将上面的函数代码写在自己的程序里是不一样的,C标准库中的memsetCache的利用做了优化,具体的在《C专家编程》151页有解释,这里给出测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <string.h>
#define MAXSIZE 100000

int main()
{
char arr[MAXSIZE];
for (int i = 0; i < 10000; i++)
{
memset(arr, 0, sizeof(arr));
//for (int j = 0; j < MAXSIZE; j++)
// arr[0] = '0';
}
return 0;
}

程序里的注释部分与memset行分别使用,结果是使用memset的程序运行时间大约为0.1s,而用for循环的程序大约需要3s。

综上:memset可以用在字符数组的初始化以及类似于memset(arr, 0, n * sizeof(int))的情况,效率比手动赋值要高的多,但大量的初始化也需要谨慎使用。