我正在使用Digital Mars C编译器在小内存模型中编写一个16位DOS程序。这个程序需要将一个文件保存到硬盘上,还需要在内存中分配64000字节来临时存储数据,以便将其与其他一些数据一起保存到硬盘。问题是,它在不该把胡言乱语保存到文件中的时候。我做了一个单独的小程序,这样我就可以把可能被窃听的代码和不应该有任何问题的代码分开。到目前为止,我已经能够将问题追溯到Digital Mars中的_fmalloc()
函数。当我写已经用_fmalloc()
分配的数据时,数据会被看似程序数据的东西破坏。当我从堆栈中分配内存时,没有问题,程序也能正常工作。
.
文件测试程序
这是我编写的小程序,目的是消除代码中可能存在的问题,并找出问题所在
#include <io.h>
#include <sysstat.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
int main(int argc, char *argv[])
{
//unsigned char data[256]; <-- using this way works just fine
unsigned char __far *data; <-- this way does not work
data = _fmalloc(256);
if(data==NULL)
{
printf("Not enough memory.n");
exit(1);
}
//initialize data to 1
_fmemset(data, 1, 256);
int file; //file handle
unsigned int err = 0;
// Create File
file = _creat("file.bin", _S_IREAD | _S_IWRITE);
if(file==-1){
printf("Error creating file.n");
exit(1);
}else{
printf("File created successfully.n");
err = 0;
}
// Write Data to File
int bytes_written = 0;
bytes_written = _write(file, &data, 256);
if(bytes_written==-1){
printf("Error writing to file.n");
exit(1);
}else{
if(bytes_written == 256){
printf("File written successfully.n");
err = 0;
}else{
printf("Error wrong amount of data written.n");
exit(1);
}
}
// Close File
err = _close(file);
if(err==-1){
printf("Error closing file.n");
exit(1);
}else{
printf("File closed.n");
}
_ffree(data);
return 0;
}
。
文件输出
使用data[256];
这就是输出的样子。总共256字节
01 01 01 01 02 01 01 0101 01 01 0101 01 01 0101 01 01 0101 01 01 0101 01 01 0101 01 01 0101 01 01 0101 01 01 0101 01 01 0101 01 01 0101 01 01
使用data = _fmalloc(256);
NO BUENO!请注意,这还会产生一个额外的字节,总共257个字节
00 00 00 38 14 00 01 05 00 00 00 00 00E5 02 A2 01 01 00 1F 01 7E2B 43 3A 5C 46 49 4C 45 2E 45 58 45 00 3C 00 50 41 54 48 3D 5A 3A 5C00 43 4F 4D 53 50 45 43 3D 5A 5C 43 4F 4D 4D 41 4E 44 2E 43 4F 4D00 42 4C 41 53 54 45 52 3D 41 32 30 20 49 35 20 44 31 20 48 35 2054 36 00 00 0D 0A 00 42 2B 4B 2B 62 2B 00 06 09 6A D4 00 00 00 0000 00 00 0000 00 00 0000 00 00 0000 00 00 0000 00 00 0000 00 00 0000 00 00 00
文本视图
。。8………….å.¢…..~+C:\FILE.EXE。<。PATH=Z:.COMSPEC=Z:\COMMAND.COM.BLASTER=A220I5 D1 H5
T6…………..B+K+B+………j
_write(file, &data, 256)
对于malloc
情况需要是_write(file, data, 256)
。它适用于自动分配的情况,因为data
和&data
对于数组是相同的地址(尽管类型不同)。
因此,我最终需要_write()和_read()函数的_far版本。编译器没有提供这些,所以我自己做了。如果这些看起来有点粗糙,请原谅我,但我以前从未做过这样的事情,所以这对我来说是一次很好的学习经历。以下是我想出的代码。
int _fwrite(int fd, void __far *buffer, unsigned int length)
{
void *buffer_offset = 0; // used to pass the offset of buffer to _write()
unsigned int buffer_segment = 0; // used to change the value of the data segment register (DS)
unsigned long int buffer_copy = 0; // used for temporary, corruptible storage of buffer
int result = 0; // used for returning number of bytes written or an error from _write()
// store the full __far pointer buffer in buffer_copy as a 32 bit int so that we can use bitwise operators
buffer_copy = (unsigned long int)buffer;
// left shift the address stored in buffer_copy two bytes to get the offset address
buffer_offset = (void *)(buffer_copy << 16);
// right shift the address stored in buffer_copy two bytes to get the segment address
buffer_segment = buffer_copy >> 16;
// change the data segment register to the one that buffer points to so that _write() will work on far data
asm
{
push ds
mov ds,buffer_segment
}
// do the file write
result = _write(fd, buffer_offset, length);
// restore previous data segment register value so that near data can be accessed again
asm
{
pop ds
}
return result;
}
int _fread(int fd, void __far *buffer, unsigned int length)
{
void *buffer_offset = 0; // used to pass the offset of buffer to _read()
unsigned int buffer_segment = 0; // used to change the value of the data segment register (DS)
unsigned long int buffer_copy = 0; // used for temporary, corruptible storage of buffer
int result = 0; // used for returning number of bytes written or an error from _read()
// store the full __far pointer buffer in buffer_copy as a 32 bit int so that we can use bitwise operators
buffer_copy = (unsigned long int)buffer;
// left shift the address stored in buffer_copy two bytes to get the offset address
buffer_offset = (void *)(buffer_copy << 16);
// right shift the address stored in buffer_copy two bytes to get the segment address
buffer_segment = buffer_copy >> 16;
// change the data segment register to the one that buffer points to so that _read() will work on far data
asm
{
push ds
mov ds,buffer_segment
}
// do the file read
result = _read(fd, buffer_offset, length);
// restore previous data segment register value so that near data can be accessed again
asm
{
pop ds
}
return result;
}