C语言Windows下llseek函数如何使用?

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

_llseek 是什么?

_llseek 是一个在Windows平台上用于移动文件指针的函数,它的名字来源于 "Large File Seek",即“大文件寻址”,它被设计用来处理超过2GB的大文件,因为它使用64位(long long)来表示文件偏移量。

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

重要提示: _llseek 是一个非标准的、特定于Windows的函数,它主要用于16位Windows的向后兼容,以及在32位Windows上处理大文件,在现代的64位Windows程序中,有更好的、更标准的替代方案。

函数原型

_llseek 通常在 <io.h><direct.h> 头文件中声明。

#include <io.h> // 或者 #include <direct.h>
long long _llseek(
  int      handle,    // 文件句柄
  long long offset,   // 移动的偏移量
  long     *origin   // 移动的起始位置
);

参数详解

  1. handle (文件句柄)

    • 这是一个整数,代表一个已打开的文件,它不是像C标准库中的 FILE* 指针,而是通过操作系统级别的函数(如 _open_creat)获取的。
    • int hFile = _open("myfile.dat", _O_RDWR | _O_BINARY);
  2. offset (偏移量)

    c语言Windowsllseek
    (图片来源网络,侵删)
    • 这是一个 long long 类型的值,表示要移动的字节数。
    • 它可以是正数(向文件末尾方向移动)或负数(向文件开头方向移动)。
  3. origin (起始位置)

    • 这是一个指向 long 类型的指针,它指定了 offset 的计算起始点,它必须是以下三个预定义宏之一:
      • SEEK_SET (值为 0): 从文件开头开始计算。
        • _llseek(hFile, 1024, SEEK_SET); // 将指针移动到文件的第1024字节处。
      • SEEK_CUR (值为 1): 从文件指针的当前位置开始计算。
        • _llseek(hFile, 512, SEEK_CUR); // 从当前位置向后移动512字节。
      • SEEK_END (值为 2): 从文件末尾开始计算。
        • _llseek(hFile, -256, SEEK_END); // 从文件末尾向前移动256字节。

返回值

  • 成功时: 返回文件指针移动后的绝对位置(从文件开头到当前位置的字节数),这个值也是一个 long long 类型。
  • 失败时: 返回 -1,并且可以通过调用 perror("_llseek")GetLastError() 来获取具体的错误信息。

完整示例代码

下面是一个完整的例子,演示如何使用 _open 打开一个文件,用 _llseek 移动指针,用 _read 读取数据,最后用 _close 关闭文件。

#include <stdio.h>
#include <io.h>      // 用于 _open, _read, _close, _llseek
#include <stdlib.h>  // 用于 exit, perror
#include <string.h>  // 用于 strlen
#define BUFFER_SIZE 256
int main() {
    const char* filename = "test_llseek.dat";
    int hFile;
    long long file_position;
    char buffer[BUFFER_SIZE];
    long origin = SEEK_SET; // 定义起始位置
    // 1. 创建并打开一个文件用于读写,如果存在则覆盖
    // _O_CREAT: 如果文件不存在则创建
    // _O_TRUNC: 如果文件存在则清空
    // _O_RDWR: 读写模式
    // _O_BINARY: 以二进制模式打开(非常重要!)
    hFile = _open(filename, _O_CREAT | _O_TRUNC | _O_RDWR | _O_BINARY, 0666);
    if (hFile == -1) {
        perror("Error opening file");
        return 1;
    }
    // 2. 向文件中写入一些初始数据
    const char* initial_data = "Hello, this is a test file for _llseek.";
    int bytes_written = _write(hFile, initial_data, strlen(initial_data));
    if (bytes_written == -1) {
        perror("Error writing to file");
        _close(hFile);
        return 1;
    }
    printf("Wrote %d bytes to '%s'\n", bytes_written, filename);
    // 3. 第一次使用 _llseek:移动到文件开头
    file_position = _llseek(hFile, 0, &origin);
    if (file_position == -1) {
        perror("Error seeking to beginning");
        _close(hFile);
        return 1;
    }
    printf("Seeked to beginning. New position: %lld\n", file_position);
    // 4. 从当前位置(开头)读取数据
    int bytes_read = _read(hFile, buffer, BUFFER_SIZE - 1);
    if (bytes_read == -1) {
        perror("Error reading from file");
        _close(hFile);
        return 1;
    }
    buffer[bytes_read] = '\0'; // 确保字符串正确终止
    printf("Read from position 0: \"%s\"\n", buffer);
    // 5. 第二次使用 _llseek:从当前位置向后移动6个字节
    // "Hello, " 是6个字符
    file_position = _llseek(hFile, 6, SEEK_CUR);
    if (file_position == -1) {
        perror("Error seeking forward");
        _close(hFile);
        return 1;
    }
    printf("Seeked forward 6 bytes. New position: %lld\n", file_position);
    // 6. 从新位置再次读取数据
    bytes_read = _read(hFile, buffer, BUFFER_SIZE - 1);
    if (bytes_read == -1) {
        perror("Error reading from file");
        _close(hFile);
        return 1;
    }
    buffer[bytes_read] = '\0';
    printf("Read from position 6: \"%s\"\n", buffer);
    // 7. 第三次使用 _llseek:从文件末尾向前移动5个字节
    // "file." 是5个字符
    file_position = _llseek(hFile, -5, SEEK_END);
    if (file_position == -1) {
        perror("Error seeking from end");
        _close(hFile);
        return 1;
    }
    printf("Seeked backward 5 bytes from end. New position: %lld\n", file_position);
    // 8. 从新位置读取数据
    bytes_read = _read(hFile, buffer, BUFFER_SIZE - 1);
    if (bytes_read == -1) {
        perror("Error reading from file");
        _close(hFile);
        return 1;
    }
    buffer[bytes_read] = '\0';
    printf("Read from near end: \"%s\"\n", buffer);
    // 9. 关闭文件句柄
    _close(hFile);
    return 0;
}

现代Windows编程的替代方案

虽然 _llseek 可以工作,但在现代C/C++开发中,尤其是在64位Windows上,强烈推荐使用以下更标准、更强大的函数:

A. C标准库的 fseekoftello

这些是C标准库(C99标准引入)的一部分,是处理大文件的标准方法,它们与 fseekftell 类似,但使用 off_t 类型来表示文件偏移量,在支持大文件的系统上,off_t 通常是64位的。

  • fseeko(FILE *stream, off_t offset, int whence)
  • ftello(FILE *stream) -> off_t

优点:

  • 可移植性: 是C标准的一部分,在Linux、macOS等现代系统上也能正常工作。
  • 易用性: 与熟悉的 fseek/ftell API风格一致。
#include <stdio.h>
FILE *fp = fopen("myfile.dat", "rb+");
if (fp) {
    // 移动到文件末尾
    fseeko(fp, 0, SEEK_END);
    // 获取当前位置(即文件大小)
    off_t file_size = ftello(fp);
    printf("File size: %lld\n", (long long)file_size);
    // 移动到文件开头
    fseeko(fp, 0, SEEK_SET);
    fclose(fp);
}

B. Windows API 的 SetFilePointerEx

这是最底层、功能最强大的Windows API函数,如果你需要直接与操作系统交互,或者需要 fseeko 没有的高级功能(如移动高精度指针),这是最佳选择。

  • BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)

优点:

  • 最底层: 直接操作Windows内核对象句柄。
  • 功能强大: 使用 LARGE_INTEGER 结构,可以精确处理64位偏移,并且可以处理32位文件指针的溢出问题。
  • 错误处理: 返回BOOL,失败时可以通过 GetLastError() 获取详细错误码。
#include <windows.h>
#include <stdio.h>
int main() {
    HANDLE hFile = CreateFile("myfile.dat",
                              GENERIC_READ | GENERIC_WRITE,
                              0,
                              NULL,
                              OPEN_ALWAYS,
                              FILE_ATTRIBUTE_NORMAL,
                              NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("Error opening file. Error code: %d\n", GetLastError());
        return 1;
    }
    LARGE_INTEGER liDistance, liNewPosition;
    liDistance.QuadPart = 1024; // 移动1024字节
    // 从文件开头移动
    if (SetFilePointerEx(hFile, liDistance, &liNewPosition, FILE_BEGIN)) {
        printf("New position: %lld\n", liNewPosition.QuadPart);
    } else {
        printf("Error seeking. Error code: %d\n", GetLastError());
    }
    CloseHandle(hFile);
    return 0;
}

总结与建议

特性 _llseek fseeko / ftello SetFilePointerEx
标准性 非标准 (Windows特有) C标准 (可移植) Windows API (Windows特有)
偏移类型 long long off_t (通常是64位) LARGE_INTEGER (64位)
句柄类型 C库句柄 (int) FILE* Windows句柄 (HANDLE)
可移植性
推荐场景 维护旧代码,或需要特定16位兼容性时 首选,大多数现代C/C++应用 需要底层控制、高性能或特殊功能时

最终建议:

  • 对于新的Windows项目: 优先使用 fseeko / ftello,它既解决了大文件问题,又保持了代码的可移植性。
  • 如果你需要直接使用Windows API句柄 (HANDLE):使用 SetFilePointerEx,它是功能最完整、最现代的Windows API。
  • 什么时候用 _llseek 几乎不需要,除非你在维护一个非常古老的、基于16位Windows或早期32位Windows的代码库,并且你不想改动太多底层I/O逻辑,对于任何新代码,都应该避免使用它。
-- 展开阅读全文 --
头像
织梦系统默认版为何生成htm文件?
« 上一篇 2025-12-20
织梦小说模块如何自动更新HTML?
下一篇 » 2025-12-20

相关文章

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

目录[+]