在Z80机器代码中,一种将缓冲区初始化为固定值的廉价技术,比如所有空白.所以一大堆代码可能看起来像这样.
LD HL, DESTINATION ; point to the source LD DE, DESTINATION + 1 ; point to the destination LD BC, DESTINATION_SIZE - 1 ; copying this many bytes LD (HL), 0X20 ; put a seed space in the first position LDIR ; move 1 to 2, 2 to 3...
结果是DESTINATION的内存块完全填满了空白.我已经尝试了memmove和memcpy,并且无法复制这种行为.我希望memmove能够正确地完成它.
memmove和memcpy为什么会这样?
有没有合理的方法来进行这种数组初始化?
我已经知道char array [size] = {0}用于数组初始化
我已经知道memset将为单个字符完成工作.
还有什么其他方法可以解决这个问题?
memmove
并且memcpy
不要那样工作,因为它不是用于移动或复制内存的有用语义.在Z80中能够填充内存很方便,但为什么你会期望一个名为"memmove"的函数用单个字节填充内存?这是为了移动内存块.无论块如何重叠,它都是为了得到正确的答案(源字节移动到目的地)而实现的.它有助于为移动内存块获得正确的答案.
如果你想填充内存,请使用memset,它可以满足您的需求.
使用堆栈可以更快地消隐内存区域.尽管LDI和LDIR的使用非常普遍,但David Webb(以各种方式推动ZX Spectrum,如包括边界在内的全屏幕倒计时)提出了这种技术,速度提高了4倍:
保存堆栈指针,然后将其移动到屏幕的末尾.
将HL寄存器对加载为零,
进入一个巨大的循环推动HL进入堆栈.
堆栈向上移动屏幕并向下移动通过内存,在此过程中,清除屏幕.
上面的解释取自David Webbs游戏Starion的评论.
Z80例程可能看起来像这样:
DI ; disable interrupts which would write to the stack. LD HL, 0 ADD HL, SP ; save stack pointer EX DE, HL ; in DE register LD HL, 0 LD C, 0x18 ; Screen size in pages LD SP, 0x4000 ; End of screen PAGE_LOOP: LD B, 128 ; inner loop iterates 128 times LOOP: PUSH HL ; effectively *--SP = 0; *--SP = 0; DJNZ LOOP ; loop for 256 bytes DEC C JP NZ,PAGE_LOOP EX DE, HL LD SP, HL ; restore stack pointer EI ; re-enable interrupts
但是,这个例程的速度要快两倍.LDIR每21个周期复制一个字节.内循环每24个循环复制两个字节 - 11个循环PUSH HL
,13 个循环DJNZ LOOP
.要获得近4倍的速度,只需展开内循环:
LOOP: PUSH HL PUSH HL ... PUSH HL ; repeat 128 times DEC C JP NZ,LOOP
这是每两个字节非常接近11个周期,比每个LDIR字节的21个周期快约3.8倍.
毫无疑问,这项技术已被多次重新发明.例如,它出现在1980年的子逻辑的飞行模拟器1中,用于TRS-80.
我相信这符合C和C++的设计理念.正如Bjarne的Stroustrup的一次说的C++设计的主要指导原则之一就是"你不要用什么,你不支付".而丹尼斯里奇可能没有用完全相同的词语说出来,我相信这也是一个指导原则,告知他的C设计(以及后来人的C设计).现在您可能会认为如果您分配内存,它应该自动初始化为NULL,我倾向于同意您的看法.但这需要机器周期,如果你在每个周期都很关键的情况下进行编码,这可能不是一个可接受的权衡.基本上C和C++试图避开你的方式 - 因此如果你想要初始化的东西,你必须自己做.
memmove和memcpy为什么会这样?
可能是因为没有针对Z80硬件的特定的现代C++编译器?写一个.;-)
这些语言没有指定给定硬件如何实现任何东西.这完全取决于编译器和库的程序员.当然,为每个可以想象的硬件配置编写一个自己的,高度指定的版本是很多工作.那就是原因.
有没有合理的方法来进行这种数组初始化?有没有合理的方法来进行这种数组初始化?
好吧,如果一切都失败了,你总是可以使用内联汇编.除此之外,我期望std::fill
在良好的STL实施中表现最佳.是的,我完全清楚我的期望太高,而且std::memset
在实践中往往表现更好.
您展示的Z80序列是最快的方式 - 1978年.那是30年前.从那时起,处理器已经取得了很大进展,而今天这是最慢的方式.
Memmove设计为在源和目标范围重叠时工作,因此您可以将一块内存向上移动一个字节.这是C和C++标准指定行为的一部分.Memcpy未指定; 它可能与memmove完全相同,或者可能有所不同,具体取决于编译器决定如何实现它.编译器可以自由选择比memmove更有效的方法.