首页 > 试题广场 >

库函数memset的原型如下: void *memset(v

[问答题]
库函数memset的原型如下:
void *memset(void *s,int c, size_t n);
这个函数将从s开始的n个字节的内存区域都填充为c的低位字节。例如,通过将参数c设置为0,可以用这个函数来对一个内存区域清零,不过用其他值也是可以的。
下面是memset最直接的实现:
/* Basic implementation of memset */
void *basic_ memset(void *s, int c, size_ t n)
{
    size_t cnt=0;
    unsigned char *schar =s;
    while (cnt<n) {
        *schar++ = (unsigned char) c;
        cnt++;
    }
    return s;
}
实现该函数一个更有效的版本,使用数据类型为unsigned long的字来装下8个c,然后用字级的写遍历目标内存区域。你可能发现增加额外的循环展开会有所帮助。在我们的参考机上,能够把CPE从直接实现的1. 00降低到0.127。即,程序每个周期可以写8个字节。
这里是一些额外的指导原则。在此,假设K表示你运行程序的机器上的sizeof (unsignedlong)的值。
●你不可以调用任何库函数。
●你的代码应该对任意n的值都能工作,包括当它不是K的倍数的时候。你可以用类似于使用循环展开时完成最后几次迭代的方法做到这一点。
●你写的代码应该无论K的值是多少,都能够正确编译和运行。使用操作sizeof来做到这一点。
●在某些机器上,未对齐的写可能比对齐的写慢很多。(在某些非x86机器上,未对齐的写甚至可  能会导致段错误。)写出这样的代码,开始时直到目的地址是K的倍数时,使用字节级的写,然后进行字级的写,(如果需要)最后采用用字节级的写。
●注意cnt足够小以至于一些循环上界变成负数的情况。对于涉及sizeof运算符的表达式,可以用无符号运算来执行测试。
#include<iostream>
using namespace std;
void* basic_memeset(void* s, int c, size_t n) {
	//一个unsigned long的变量装4个c,采用union方式(本机unsigned long为4字节)
	union T { unsigned long c1; unsigned char c2[sizeof(unsigned long)]; }x;
	for (int i = 0; i < sizeof(unsigned long); i++) {
		x.c2[i] = unsigned char(c);
	}

	size_t cnt = 0;
	//void *赋值给其他指针需要强制转换
	unsigned long* schar = (unsigned long*)s;
	while (cnt < n / sizeof(unsigned long)) {
		*schar++ = x.c1;
		cnt++;
	}
	size_t cnt1 = 0;
	unsigned char* schar1 = (unsigned char*)s+cnt* sizeof(unsigned long);
	while (cnt1 < n % sizeof(unsigned long)) {
		*schar1++ = x.c2[0];
		cnt1++;
	}
	return s;
}
int main()
{
	int buffer[10];
	for (int i = 0; i < 10; i++) {
		buffer[i] = 0;
	}
	basic_memeset(buffer, 1, 30);
	for (int i = 0; i < 10; i++) {
		cout << buffer[i] << endl;
	}
	return 0;
}

发表于 2021-12-01 17:19:10 回复(0)