movedata函数如何正确使用?

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

movedata 是一个在 16位实模式(例如早期的 MS-DOS 环境)下使用的、用于在内存块之间快速复制数据的函数,它在现代的 32位或 64位保护模式操作系统(如 Windows, Linux, macOS)中已经不再使用,并且如果强行使用会导致程序崩溃。

c语言movedata
(图片来源网络,侵删)

函数原型

movedata 函数通常在 <dos.h><mem.h> 头文件中声明。

void movedata(unsigned srcseg, unsigned srcoff, unsigned destseg, unsigned destoff, unsigned numbytes);

参数详解

movedata 的独特之处在于它不使用普通的指针(如 void*),而是使用段:偏移地址来定位内存,这是由 16位 x86 架构的内存分段机制决定的。

  • unsigned srcseg: 源数据的段地址,这是一个 16位的值,表示内存块的起始段。
  • unsigned srcoff: 源数据的偏移地址,这是一个 16位的值,表示在段内的具体位置。
  • unsigned destseg: 目标数据的段地址,内存块要复制到的起始段。
  • unsigned destoff: 目标数据的偏移地址,在目标段内的具体位置。
  • unsigned numbytes: 要复制的字节数

内存地址计算方式: 实际的物理地址 = 段地址 * 16 + 偏移地址。 段地址为 0x1000,偏移地址为 0x0050 的物理地址是 0x1000 * 16 + 0x0050 = 0x10050

功能

该函数的功能是:从 srcseg:srcoff 指定的内存位置,复制 numbytes 个字节的数据到 destseg:destoff 指定的内存位置。

c语言movedata
(图片来源网络,侵删)

它的一个重要特性是:它可以跨段复制数据,并且使用了底层指令,速度比用循环逐字节复制要快得多。

使用示例 (DOS 环境)

这是一个在 Turbo C 或 Borland C++ 等 16位编译器下运行的 DOS 程序示例,它会将一块内存中的数据复制到另一块内存中,然后打印出来。

#include <stdio.h>
#include <dos.h> // 包含 movedata 的声明
int main() {
    // 定义源和目标的段:偏移地址
    unsigned source_segment = 0x1000; // 假设源数据在 0x1000 段
    unsigned source_offset = 0x0000;  // 从段的开头开始
    unsigned dest_segment = 0x2000;   // 假设目标在 0x2000 段
    unsigned dest_offset = 0x0000;    // 从段的开头开始
    // 要复制的字节数
    unsigned num_bytes = 10;
    // 1. 在源内存块中写入一些数据
    // 注意:在实模式下,直接通过段:偏移访问内存需要特殊处理或汇编
    // 这里我们用 movedata 的“反向操作”来初始化源数据,以展示其用法
    char init_data[] = "Hello, DOS!";
    // 将初始化数据从代码段(由 FP_SEG 获取)复制到源内存块
    movedata(FP_SEG(init_data), FP_OFF(init_data), source_segment, source_offset, sizeof(init_data));
    printf("Data has been moved from segment 0x%04X to segment 0x%04X\n", source_segment, dest_segment);
    // 2. 将数据从源内存块复制到目标内存块
    movedata(source_segment, source_offset, dest_segment, dest_offset, num_bytes);
    // 3. 从目标内存块读取并打印数据
    char buffer[20];
    movedata(dest_segment, dest_offset, FP_SEG(buffer), FP_OFF(buffer), num_bytes);
    buffer[num_bytes] = '\0'; // 确保字符串正确终止
    printf("Data in destination buffer: %s\n", buffer);
    return 0;
}

如何编译和运行 (仅适用于DOS环境): 你需要在支持 16位 DOS 代码的编译器(如 Turbo C++ 3.0)中编译此代码,并在 DOS 模拟器(如 DOSBox)中运行。

重要警告:为什么在现代 C/C++ 中不能使用?

movedata 是一个过时且危险的函数,在现代编程中必须避免使用,原因如下:

c语言movedata
(图片来源网络,侵删)
  1. 实模式 vs. 保护模式

    • DOS (实模式):内存是平坦的,程序可以访问任何内存地址(0x000000xFFFFF),movedata 就是在这个模型下工作的。
    • 现代操作系统 (保护模式):内存是虚拟受保护的,每个进程都有自己的、独立的虚拟地址空间(在 32位系统上是 4GB),操作系统通过内存管理单元和页表将虚拟地址映射到物理内存。
    • movedata 使用的 seg:off 地址是物理地址,在保护模式下,你的程序根本不知道物理地址是什么,也无法直接访问它们,试图使用 movedata 会传递一个无效的段地址,导致 CPU 触发一个通用保护故障,操作系统会立即终止你的程序。
  2. 内存管理

    • 在现代 C/C++ 中,你通过 malloc, new, calloc 等函数向操作系统申请内存块,操作系统返回给你的是一个虚拟地址指针(如 void*char*)。
    • 你应该使用标准库函数如 memcpy 来在这些由 malloc 分配的内存块之间复制数据。
  3. 安全性

    • movedata 可以复制任何地方的内存,包括操作系统的内核空间,极其危险。
    • memcpy 是类型安全的,它接受 void* 指针,并且其作用范围严格限定在进程自己的虚拟地址空间内,由编译器和操作系统共同管理,安全得多。

现代替代方案:memcpy

在所有现代 C/C++ 编程中,你应该使用 memcpy 函数。

memcpy 的原型:

#include <string.h> // 包含 memcpy 的声明
void *memcpy(void *dest, const void *src, size_t num);

memcpy 的特点:

  • 安全:在进程自己的虚拟地址空间内操作。
  • 高效:编译器通常会将其优化为高效的机器指令(如 rep movsb)。
  • 标准:是 C/C++ 标准库的一部分,可移植性极强。

现代 C/C++ 编程示例:

#include <stdio.h>
#include <string.h> // 使用 memcpy
#include <stdlib.h> // 使用 malloc
int main() {
    // 1. 分配源内存和目标内存
    char *src_buffer = (char*)malloc(100);
    char *dest_buffer = (char*)malloc(100);
    if (src_buffer == NULL || dest_buffer == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }
    // 2. 在源内存中写入数据
    strcpy(src_buffer, "This is a modern way to copy data.");
    // 3. 使用 memcpy 复制数据
    //    dest_buffer: 目标指针
    //    src_buffer:  源指针
    //    50:         要复制的字节数
    memcpy(dest_buffer, src_buffer, 50);
    // 4. 打印结果
    printf("Source buffer: %s\n", src_buffer);
    printf("Destination buffer (first 50 bytes): %s\n", dest_buffer);
    // 5. 释放内存
    free(src_buffer);
    free(dest_buffer);
    return 0;
}
特性 movedata memcpy
时代 过时,用于 16位 DOS 实模式 现代标准,用于所有保护模式系统
地址模型 物理地址 (seg:off) 虚拟地址 (void*)
头文件 <dos.h>, <mem.h> <string.h>
安全性 极不安全,可访问任意物理内存 安全,在进程虚拟空间内操作
可移植性 ,仅限 x86 架构的 DOS 极佳,所有 C/C++ 平台都支持
推荐使用 绝对不要在新的代码中使用 必须使用,是复制内存块的标准方法

movedata 是一个历史遗迹,了解它有助于理解计算机发展史,但在任何现代软件开发项目中,都应该使用 memcpy 来完成内存块复制的任务。

-- 展开阅读全文 --
头像
织梦火车头发布模块如何高效对接?
« 上一篇 04-17
c语言defaults
下一篇 » 04-17

相关文章

取消
微信二维码
支付宝二维码

目录[+]