荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: kaman (天道酬勤), 信区: ACMICPC
标  题: 关于 memset 的使用技巧 zz
发信站: 荔园晨风BBS站 (Sun Nov 19 13:56:40 2006), 站内

相信大家都很喜欢使用 memset 来初始化数组. 不仅仅因为方便, 而且因为一般情况下,它
是比较高效的.
但是,有一个让我们比较不满意的是, 当 memset 初始化数组的时候, 我们只能将元素初始
化为 0(0x0000000) 或者 -1(0xffffffff).
经过测试,发现其实 memset 可以初始化为出了 0, -1 以外的数值的 ---- ( [-128, 127]
 或者 [0, 255] )

那该如何实现呢?

今天, 在测试关于全局和局部变量内存分配以及数组内存访问效率的时候, 顺便测试了一下
 memset.
由于, 我并没有正式的学过汇编(好像大二就有汇编课了,呵呵! 很高兴 ---- 因为, 懂了汇
编, 可以干很多事, 也可以对某些比较底层的原理进行探索哈), 认识的命令也十分有限,
所以就不能十分详细地为大家分析 memset 对于初始化数的具体过程了(虽然相关的汇编语
句还不到 10 条, 希望达人们能够给以补充,3x~~~~ :-)
下面是我 debug 时 copy 的 memset 关于初始化数据处理的汇编代码:

0040824C   xor         eax,eax
0040824E   mov         al,byte ptr [esp+8]
....
00408269   mov         ecx,eax
0040826B   shl         eax,8
0040826E   add         eax,ecx
00408270   mov         ecx,eax
00408272   shl         eax,10h
00408275   add         eax,ecx

最后真正用于初始化的就是 EAX 寄存器里的数值.
原本, 我以为 memset 是通过一次性大片内存初始化来提高速度的.但是, 通过调试,我发现
自己错了, 它是 4 个字节 4 个字节初始化的.

下面稍微分析一下初始化数据的处理过程:
在 0040824E, 我们可以发现, memset 实际上需要的仅仅是我们传给它的初始化数值的低位
( 1 Byte).
假设我们传入的数值是: 0xabcd1234 , 则第一次取低位存到 EAX 的 al (低位) 以后, 得
到的应该是 0x00000034.
然后, 通过在寄存器 EAX, ECX 进行 mov, shl, add 相关操作, 使原本 EAX 的低位 al 进
行了扩充, 使得对于每一个字节存储的值和 al 一致.
比如,刚才的 0x00000034 经过运算之后, memset 开始初始化需要填充的数值就是
0x34343434.
于是, 我们待初始化的所有整数都被初始化为 0x34343434, 而不是我们需要的
0xabcd1234.

至此, 我们对于整数的初始化问题就得到了回答:
我们之所以可以成功初始化各个元素为 0 或者 -1, 是因为这两个数值的特殊性.
首先是 0 , 也就是 0x00000000, 可见经过以上的运算之后, 得到的最后用于初始化的 4
字节单元还是 0x00000000.
然后看-1 , 也就是 0xffffffff, 同样, 经过以上运算之后, 得到的最后用于初始化的 4
字节单元还是 0xffffffff.

总的来说, 可以概括地说, memset 的功能是对目标内存的每一个字节进行初始化为相同的
值.
那么,我们很自然可以想到,我们可以利用一个字节的变量 memset 来初始化除了 0 和
-1 以外的数.
当然, 这种功能是有前提的, 因为一个字节的只能表示相当有限的数.
所以你的初始化范围必须要在 [0x00, 0xff] 的范围内, 用十进制表示,也就是,能够初始化
的范围是: [0, 255]
但是,如果我们可以灵活运用 C++ 提供的那些 One Byte 的数据类型的话,可以使范围进
行一些拓展.

下面我就举几个例子来描述一下:

一: 我们可以用于初始化字符串:
char pszStr_F[44];
memset(pszStr_F, 'F', sizeof(pszStr_F)-1);
pszStr_F[43] = 0;
cout <<pszStr_F <<endl;

output:
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

二: 初始化为 [0, 255] 范围内的整数:
这个需要比较仔细选择代表数据类型. 自然不可以用 int, 也不可以用 short.
刚开始我想到了 char, 但是后来发现不行, 因为 C++ 的 char 是有符号的, 也就是说, 它
最大表示的整数只是 2^7 - 1 (即 127).
但也,也正因为它的存在, 使我们可以让初始化范围又增加了 ---- 还可以来用来初始化为
[-128, 0] 的数(在后面介绍)
既然因为 char 带符号而不行,很自然地想到了 unsigned char, 没错,它是可以!
其实,还有一个数据类型 ---- bool,实际上 bool 也占用了一个字节的数值!
样例 二 - A :
unsigned char oneByteArray[44];
memset(oneByteArray, 244, sizeof(oneByteArray));
for (int nPos = 0; nPos < 44; nPos++)
cout <<int(oneByteArray[nPos]) <<" ";
cout <<endl;

output:
244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244
244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244 244
244 244 244 244
Press any key to continue

样例 二 - B : 思考一下,为什么会输出这结果呢? :-)
bool oneByteArray[44];
memset(oneByteArray, -1, sizeof(oneByteArray));
for (int nPos = 0; nPos < 44; nPos++)
cout <<int(oneByteArray[nPos]) <<" ";
cout <<endl;

output:
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255
Press any key to continue

三: 初始化为 [-128, 127] 范围内的整数:
在二中说过了, 这是利用了 char 的带符号的特性
char oneByteArray[44];
memset(oneByteArray, -44, sizeof(oneByteArray));
for (int nPos = 0; nPos < 44; nPos++)
cout <<int(oneByteArray[nPos]) <<" ";
cout <<endl;

output:
-44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44
-44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44 -44
-44 -44 -44 -44
Press any key to continue


Author : Henry_Four
E-Mail : Henry_Four@163.com
Blog : http://henryfour.bokee.com
Time : 2006-08-09 02:34
Environment : MS-Visual C++ 6.0

--

     Science is what we understand well enough to explain to a computer.
     Art is everything else we do.

                                                 ———— Donald E. Knuth



※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 210.21.224.233]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店