「Linux 系统编程」lseek 函数基本使用方法

1 lseek 函数

1.1 lseek 函数简介

lseek 函数的作用是 重新定位文件的读写偏移量

每个打开的文件都有一个关联的 当前文件的读写偏移量。这个偏移量是一个非负整数,用于指示从 文件开始处到当前读写位置的字节数。读(read)和写(write)操作都从这个偏移量开始,并在成功完成后自动更新这个偏移量,增加实际读写的字节数。

lseek 函数可以设置这个偏移量到文件的任意位置。实现了 随机访问(Random Access) 文件的能力。


1.2 lseek 函数原型

1
2
3
4
#include <sys/types.h>  // off_t
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

off_t 全称是 Offset Type,它是一个用于表示文件大小和文件偏移量(位置)的数据类型。通常是 64 位的有符号整数。

  • fd:通过 open 函数打开文件时返回的 文件描述符

  • offset:一个 偏移量,其含义取决于第三个参数 whence

  • whence:一个 定位基准点。可以取以下三个值之一:

    • SEEK_SET:将文件的读写偏移量设置为 从文件开始处 向后偏移 offset 个字节。

    • SEEK_CUR:将文件的读写偏移量设置为 当前值 加上 offset 个字节。offset 可以是正数(向后移动),也可以是负数(向前移动)。

    • SEEK_END:将文件的读写偏移量设置为从 文件末尾 向后偏移 offset 个字节。offset 同样可以是正数或负数。


1.3 lseek 函数返回值

成功:返回 新的文件偏移量(从文件开始处计算的字节数)。

失败:返回 -1,并设置全局变量 errno 以指示错误类型。



2 lseek 使用

lseek 函数的主要使用场景:

  1. 文件的 “读”、“写” 使用同一偏移位置。
  2. 使用 lseek 获取文件大小。
  3. lseek 在文件末尾之后移动偏移量(创建“空洞文件”)。最后需要进行写的操作

2.1 lseek 基本使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

#define BUFFER_SIZE 64

int main(){
int fd;
char str[] = "Alice Margatroid and Kirisame Marisa\n";
char buf[BUFFER_SIZE];

fd = open("marisa.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if(fd == -1){
perror("open error");
return 1;
}

write(fd, str, strlen(str));

// 若没有下面这句,读取不到任何内容
lseek(fd, 0, SEEK_SET);

int nbytes;
while((nbytes = read(fd, buf, sizeof(buf)))){
if(nbytes == -1){
perror("read error");
return 1;
}
write(STDOUT_FILENO, buf, nbytes);
}

close(fd);

return 0;
}


2.2 lseek 获取文件大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>


int main(){
int fd = open("Geometry.cpp", O_RDWR);
if(fd == -1){
perror("open error");
return 1;
}

// 文件的当前偏移量设置到文件末尾
off_t file_size = lseek(fd, 0, SEEK_END);
if(file_size == -1){
perror("lseek error");
return 1;
}

printf("file size = %lld bytes\n", (long long)file_size);

close(fd);

return 0;
}

这种方法获取文件大小后,文件的当前偏移量已经位于文件末尾,后续的读操作会直接读到 EOF。如果需要从别的地方读写,需要再次使用 lseek 调整位置。


2.3 lseek 拓展文件大小

使用 lseek 在文件末尾之后移动偏移量(创建“空洞文件”)。

将偏移量移动到文件末尾之后的某个位置,然后进行写入操作,中间的空隙会被填充为 \0(空字节),从而在文件中创建一个“空洞”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>


int main(){
int fd = open("marisa.txt", O_RDWR);
if(fd == -1){
perror("open error");
return 1;
}

// 偏移到文件末尾之后 +233 字节位置
off_t file_size = lseek(fd, 233, SEEK_END);
if(file_size == -1){
perror("lseek error");
return 1;
}

printf("file size = %lld bytes\n", (long long)file_size);

close(fd);

return 0;
}

文件大小原先为 37 字节,加上 233 个空字节(\0),最后再加上写入的 “suki” 占 4 个字节,所以表面上文件经过拓展后的大小为 274 字节。

但是,磁盘上可能不会为这些 \0 分配实际的物理块,所以这种文件看起来很大,但实际占用的磁盘空间可能很小。数据库和虚拟化软件常利用这个特性创建稀疏文件。


「Linux 系统编程」lseek 函数基本使用方法
https://marisamagic.github.io/2025/08/22/20250822_3/
作者
MarisaMagic
发布于
2025年8月22日
许可协议