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

(图片来源网络,侵删)
重要提示: _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 // 移动的起始位置 );
参数详解
-
handle(文件句柄)- 这是一个整数,代表一个已打开的文件,它不是像C标准库中的
FILE*指针,而是通过操作系统级别的函数(如_open或_creat)获取的。 int hFile = _open("myfile.dat", _O_RDWR | _O_BINARY);
- 这是一个整数,代表一个已打开的文件,它不是像C标准库中的
-
offset(偏移量)
(图片来源网络,侵删)- 这是一个
long long类型的值,表示要移动的字节数。 - 它可以是正数(向文件末尾方向移动)或负数(向文件开头方向移动)。
- 这是一个
-
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标准库的 fseeko 和 ftello
这些是C标准库(C99标准引入)的一部分,是处理大文件的标准方法,它们与 fseek 和 ftell 类似,但使用 off_t 类型来表示文件偏移量,在支持大文件的系统上,off_t 通常是64位的。
fseeko(FILE *stream, off_t offset, int whence)ftello(FILE *stream)->off_t
优点:
- 可移植性: 是C标准的一部分,在Linux、macOS等现代系统上也能正常工作。
- 易用性: 与熟悉的
fseek/ftellAPI风格一致。
#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逻辑,对于任何新代码,都应该避免使用它。
