传说,Pascal中有一个函数——fillchar。这是高手们装*用的利器,也是大家的神器(大家好才是真的好)。
百度百科说:“Fillchar是Turbo/Borland Pascal的System单元的一个标准过程,它的使用格式是:FillChar(var X; Count: Word; value),它的功能是,把指定变量X在内存段中所占的低Count个字节赋为相同的值value, 其中value是填充的值,只能是Byte、Char或Boolean等单字节类型的值。在Free Pascal中稍加扩展为FillChar(var X; Count: Longint; value), 功能没变。”听起来很没意义,搞不懂究竟是个什么鬼吧?
其实这就是个内存块赋值函数,举个例子,这里有一段代码:
fillchar(a,sizeof(a),233);//a:array[1..100] of byte
等效于:
for i:=1 to 100 do a[i]=233;
显而易见,这个函数减少了编码工作量,而且不用在写代码时抬头看数组a的范围。看到这里,你肯定认为你已经懂了,打开了FPC,键入了如下代码:
fillchar(a,sizeof(a),1);//a:array[1..100] of longint
回头用Debug会发现,a中的值不为1,而是一个很大的值(16843009)10=(0000001000000100000010000001)2。没错,你被fillchar坑了。它的工作机理并有你想象的这么简单。
回顾上文,看见红色的两个类型名了吗?再回顾上文,看见“单字节”三个字了吗?byte是个单字节类型,范围是0-255,但longint不是,它是一个4字节类型,范围是-2^31~2^31。而FPC把这个数组仍当做了一个byte等单字节类型的数组来处理。对于一个longint,其在内存中的块分布是这样的:(第1行第n列的数表示第n字节所表示的数,第2行第n列的数表示第n字节所表示数的二进制形式)
0 | 0 | 0 | 0 |
00000000 | 00000000 | 00000000 | 00000000 |
经过一次fillchar(a,sizeof(a),1)以后,fillchar对每个字节中的数都进行了赋值,于是内存就变成了这样。
1 | 1 | 1 | 1 |
00000001 | 00000001 | 00000001 | 00000001 |
把下面那行转化为10进制,就是16843009。
再看一个例子,如果是fillchar(a,sizeof(a),128)呢?
128 | 128 | 128 | 128 |
10000000 | 10000000 | 10000000 | 10000000 |
呵呵,按刚才那样的转换,结果应该是2155905152。但是Debug又能够让你傻眼,结果是-2139062144。
你忘记longint是个有符号数了吗?“1”处在符号位上,于是整个数就变成了负数。而负数又是补码来表示。当符号位为1时,补码的补码就是原数的绝对值。转一下,看看是不是这样?
这里分点来描述:
神器是有亲戚的!
pascal中,fillchar的亲戚有fillword,filldword,分别可以用在填充integer、longint数组,而且所见即所得——填充后的数与第三个参数一样(只是故意不早告诉你)。
c++中,它的亲戚有memset,用法应该与fillchar一样(但是支持有符号数作为参数)。