目 录
2.1 按要求将linkaddress.c编译链接生成执行程序,按照地址从高到低,列出所有程序符号的地址、名称、存储区、占用空间... - 3 -
3.2 结构体数据的二进制文件读写(19分)... - 5 -
第4章 补充:UNIX IO
4.1 UNIX IO的优缺点
4.2 使用UNIX IO完成实验
第1章 实验基本信息
1.1 实验目的
-
- 理解程序中的各种符号地址(虚拟地址)
- 熟练掌握文件相关的操作与程序设计(标准IO、UnixIO)
- 熟练掌握Linux下的程序调试方法
- 增强对信息和文件存储的理解
- 掌握多模块程序的编写、编译、链接方法
1.2 实验环境与工具
1.2.1 硬件环境
Intel X86-64、ARM v8鲲鹏920处理器、香橙派/树莓派
1.2.2 软件环境
Ubuntu 18.04 LTS 64以上
(或Windows/MacOS + 虚拟机VirtualBox/Vmware );
1.2.3 开发工具
gcc、g++;vi/vim/gedit
第2章 虚拟地址空间
2.1 按要求将linkaddress.c编译链接生成执行程序,按照地址从高到低,列出所有程序符号的地址、名称、存储区、占用空间
首先查看linkaddress.c程序,该程序已提供。
// 编译链接: gcc -Og -no-pie -fno-PIC -fno-omit-frame-pointer -fno-stack-protector -fcf-protection=none -mmanual-endbr -g
// -fno-PIC 共享函数与main在同一段 -fPIC 共享函数在高地址 共享函数区 分别实验看一下
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
char big_array[1L<<24]; /* 16 MB */ //.bss
char huge_array[1L<<30]; /* 1 GB */ //.bss
int gint0 =0; //初值为0,放在.bss节
int global = 0x55aa00ff; //.data
long glong=0x1122334455667788L; //.data
char cstr[100]="cstr1234567890abcdefghijklmnopqrstuvwxyz"; //放在.data节 数据段,可更改
char *pstr="rstr1234567890abcdefghijklmnopqrstuvwxyz"; //放在.rodata节 代码段 不能修改,否则出现段错误
const int gc=0x8899aabb; //.rodata
const char cc[100]="Hello!\n"; //.rodata
int useless() { return 0; } //.text
static void show_pointer(void *p, char *descr) { //.text
// printf("Pointer for %s at %p\n", descr, p);
printf("%s\t%p\t%lu\n", descr, p, (unsigned long) p);
}
int main (int argc,char *argv[]) //.text
{
char **env = __environ; //环境变量__environ
void *p1, *p2, *p3, *p4,*p5; //heap 或 共享heap
int li0 = 0;
int li1 = 1;
static int lsi0=0; //.bss
static int lsi1=1; //.data
char lc[1001]="9876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210";
char *ls="1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF";
if(argc!=4)
{
printf("Usage: LinkAddress P1 P2 P3\n");
return 1;
}
useless();
//打印系统变量信息
show_pointer((void *) env, "env");
int i=0;
while(*env)
{
printf("env[%d]\t",i);
show_pointer((void *) (*env), "*env");
puts(*env);
env++; i++;
}
p1 = malloc(1L << 28);
p2 = malloc(1L << 17);
p3 = malloc( (1L << 17) +1);
p4 = malloc(1L << 30);
p5 = malloc(1L << 31);
show_pointer((void *) big_array, "big array");
show_pointer((void *) huge_array, "huge array");
show_pointer((void *) &global, "global");
show_pointer((void *) &gint0, "gint0");
show_pointer((void *) &glong, "glong");
cstr[0]='C';
show_pointer((void *) cstr, "cstr");
// pstr[0]='R'; //rodata节在text代码段,不能修改
show_pointer((void *) pstr, "pstr");
show_pointer((void *) &gc, "gc");
show_pointer((void *) cc, "cc");
show_pointer((void *) &li0, "local int 0");
show_pointer((void *) &li1, "local int 1");
show_pointer((void *) &lsi0, "local static int 0");
show_pointer((void *) &lsi1, "local static int 1");
show_pointer((void *) lc, "local astr");
show_pointer((void *) ls, "local pstr");
show_pointer((void *) &argc, "argc");
show_pointer((void *) argv, "argv");
printf("argv[0] %16lx\n",(unsigned long) (*argv));
printf("argv[1] %16lx\n",(unsigned long) (*(argv+1))); //argv[1] argv 指针+1 就是argv地址+8
printf("argv[2] %16lx\n",(unsigned long) (*(argv+2)));
printf("argv[3] %16lx\n",(unsigned long) (*(argv+3)));
show_pointer((void *) argv[0], "argv[0]");
printf("%s\n",argv[0]);
show_pointer((void *) argv[1], "argv[1]");
printf("%s\n",argv[1]);
show_pointer((void *) argv[2], "argv[2]");
printf("%s\n",argv[2]);
show_pointer((void *) argv[3], "argv[3]");
printf("%s\n",argv[3]);
show_pointer((void *) p1, "p1");
show_pointer((void *) p2, "p2");
show_pointer((void *) p3, "p3");
show_pointer((void *) p4, "p4");
show_pointer((void *) p5, "p5");
show_pointer((void *) show_pointer, "show_pointer");
show_pointer((void *) useless, "useless");
show_pointer((void *) main, "main");
show_pointer((void *) exit, "exit");
show_pointer((void *) printf, "printf");
show_pointer((void *) malloc, "malloc");
show_pointer((void *) free, "free");
free(p1); free(p2); free(p3); free(p4); free(p5);
show_pointer((void *) strcpy, "strcpy");
return 0;
}
编译命令
~$ gcc -Og -no-pie -fno-PIC -fno-omit-frame-pointer -fno-stack-protector -fcf-protection=none -mmanual-endbr –g
linkaddress.c –o linkadress
命令行还需输入运行参数
~$ ./linkaddress <long int> <string> <long int>
这个实验主要要掌握的是,在一个c程序中,链接后生成的执行程序各语言要素(变量、参数、常量、函数等)在虚拟地址空间的分布 。
参考以上内容,可以总结,在链接过程中
代码段.txt/.rodata:可执行程序的机器代码、常量const、字符串常量。这个段是只读的。
数据段.data:数据段包括了初始化的全局变量和静态变量。这些变量在程序运行期间可以被修改。数据段通常位于代码段之后,它的地址也是可执行程序加载时确定的。
BSS段.bss:未初始化的全局变量和静态变量,它们在程序加载时被初始化为零。BSS段也位于数据段之后,它的大小在可执行程序中是固定的,但它不占用实际的磁盘空间。
堆(Heap):堆是用于动态分配内存的区域,它通常位于数据段和共享库之间的空间。堆的大小可以动态地增长和收缩,由C库函数(如malloc和free)管理。堆的起始地址通常由操作系统在程序运行时决定。
栈(Stack):栈用于存储函数的局部变量、函数的返回地址以及函数调用的上下文信息。栈通常位于虚拟地址空间的高地址部分,向低地址方向增长。栈的大小在程序运行时是动态管理的,当函数被调用时,会在栈上分配一些空间,当函数返回时,这些空间会被释放。
共享库(Shared Libraries):共享库包含了可执行程序所需的共享代码和数据,这些库通常被多个进程共享,以减少内存占用和提高效率。共享库的地址通常也由操作系统管理和加载。
第3章 文件信息处理
3.1 基本数据类型的文件读写
3.1.1文本文件读写
从文本文件读入数据,文件中每行保存着一个数据记录,包括编号id(纯数字)、姓名name、3个课程分数的数值(浮点型,0~100)、1个体重(整型),各数值之间有空格分开。将读入到内存的数据,输出到文本文件,格式同前。
读写操作
相关函数代码与说明:
#include <stdio.h>
#include <stdlib.h>
#include "fileop.h"
int main() {
FILE *file = fopen("testfile.txt", "r");
if (file == NULL) {
perror("无法打开文件");
return 1;
}
FILE *fp = fopen("test_write.txt", "w");
if (fp == NULL) {
perror("无法打开文件");
return 1;
}
fileop(file,fp);
fclose(file);
fclose(fp);
return 0;
}
讲解一下思路。首先定义一个结构体存储信息,接着从传入的文件中读取一行数据,当没有读到文件末尾时进行循环,使用sscanf 判断对应输入是否合法,是则打印到屏幕上,再使用fprintf写入文件。
//input:file:pointer of input txt;fp:pointer of output txt
//output:逐项格式化输出,write in fp
void fileop(FILE *file,FILE *fp)
{
// 定义结构体来存储每个数据记录
struct Record {
int id;
char name[50];
float scores[3];
int weight;
};
struct Record record; // 存储每行数据记录的结构体
char line[256]; // 用于存储读取的每行数据
while (fgets(line, sizeof(line), file) != NULL)
{
if (sscanf(line, "%d %49s %f %f %f %d", &record.id, record.name,
&record.scores[0], &record.scores[1], &record.scores[2], &record.weight) == 6) {
printf("%d, %s, %.1f, %.1f, %.1f, %d\n", record.id, record.name,
record.scores[0], record.scores[1], record.scores[2],record.weight);
fprintf(fp, "%d, %s, %.1f, %.1f, %.1f, %d\n", record.id,
record.name, record.scores[0],
record.scores[1], record.scores[2], record.weight);
}
}
}
用到的文件操作函数
FILE* fopen(<filename>,r/w)
其中r/w表示对文件是只读还是读写,其返回值是指向该文件的指针。在调用该函数后一般需要检查文件打开是否成功,即检查指针是否为空。注意文件要在当前工作区内。
char *fgets(char *str, int size, FILE *stream)
其中str是待存储区域的指针,stream是一个数据流,fgets函数将数据流中的内容写入到s中,但写入有限制,最多写size-1个就停止,此时在文件结尾写入结束符\0。成功读取时返回str,否则返回null,所以使用fgets时也要检查返回值。
fgets函数及其用法,C语言fgets函数详解-CSDN博客
int sscanf(const char *str, const char *format, ...);
sscanf比scanf多出的就是指定了要解析度字符串str,后面跟随其格式化形式,以及存储到什么变量里面去。其返回值是成功解析的参数数量。
int fprintf(FILE *stream, const char *format, ...);
和sscanf类似,将后续的参数按照指定的格式写入到指定的输出流stream中。返回值是成功写入的字符数,即成功写入到输出流中的字符数,如果写入失败,则返回一个负数作为错误标志。
3.1.2 二进制文件读写
(1)写操作
将1个100个元素的浮点数组、1个100个字符的字符数组分别写入到二进制文件、文本文件(共四个文件),比较文本/二进制文件的大小、内容的差异。
相关函数代码与说明:
使用时间为种子生成随机数初始化两个数组,然后分别写到二进制文件和文本文件中,写入二进制文件使用fwrite函数,写入文本文件使用fprintf函数,最后别忘了关闭四个文件
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(NULL));
float farr[100];
for (int i = 0; i<100; i++)
{
farr[i] = ((float)rand() / RAND_MAX) * 100.0;
}
char carr[100];
for (int i = 0; i<100; i++)
{
carr[i] = 'Y' + rand() % 26;
}
FILE *file1 = fopen("bin_float.bin", "wb");//wb表示二进制写
FILE *file2 = fopen("bin_char.bin", "wb");
if (file1 == NULL || file2 == NULL)
{
perror("无法打开二进制文件");
return 1;
}
fwrite(farr, sizeof(float), 100, file1);
fwrite(carr, sizeof(char), 100, file2);
fclose(file1);
fclose(file2);
// 写入文本文件
FILE *file3 = fopen("text_float.txt", "w");
FILE *file4 = fopen("text_char.txt", "w");
if (file3 == NULL || file4 == NULL)
{
perror("无法打开文本文件");
return 1;
}
for (int i = 0; i < 100; i++)
{
fprintf(file3, "%.2f\n", farr[i]);
fprintf(file4, "%c\n", carr[i]);
}
fclose(file3);
fclose(file4);
printf("wirte success!\n");
return 0;
}
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
ptr是指向待写入数据的区域的指针,size是每个写入数据的大小,可以通过sizeof获取,count是写入数据的个数,stream是被写入的文件指针。返回成功写入的数据项数量,如果写入失败或出现错误,则返回一个小于 count
的值。可以使用 ferror
函数来检查是否发生了错误,以及使用 feof
函数检查是否到达了文件的末尾。
【C 语言】文件操作 ( fwrite 函数 )_韩曙亮的博客-CSDN博客
两个结果文件的差异、你的经验体会:
①同一文件类型和数据量,浮点数文件大于字符文件,这可能是因为浮点数占用的位数更多
②同一数据量,二进制文件小于文本文件,这可能是因为二进制编码更高效
③心得:保存较大的文件时,可以尝试着保存为二进制文件减小占用
(2)读操作
从二进制文件读入100个浮点数值。
相关函数代码与说明:使用fopen函数打开文件得到文件指针。使用fread函数读取二进制文件并拷贝到farr数组中,最后打印出来
#include <stdio.h>
int main()
{
FILE *file = fopen("bin_float.bin", "rb"); // "rb" 表示以二进制读取方式打开文件
if (file == NULL)
{
perror("无法打开二进制文件");
return 1;
}
float farr[100];
//size_t size = sizeof(floatArray) / sizeof(floatArray[0]);
fread(farr, sizeof(float), 100, file);
// 读取字符数组(字符串)
//char charArray[256]; // 假设字符数组最大长度为256
// fread(charArray, sizeof(char), sizeof(charArray), file);
// 关闭文件
fclose(file);
// 打印读取的数据
for (size_t i = 0; i < 100; i++)
{
printf("%.2f\n ", farr[i]);
}
//printf("字符数组(字符串):%s\n", charArray);
return 0;
}
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
ptr是指向被写入数据的区域的指针,size是每个写入数据的大小,可以通过sizeof获取,count是写入数据的个数,stream是待写入的文件指针。返回成功写入的数据项数量,如果写入失败或出现错误,则返回一个小于 count
的值。
【C 语言】文件操作 ( fread 函数 )_fread循环读取一个文件-CSDN博客
3.2 结构体数据的二进制文件读写
3.2.1写操作
将结构体数组写入二进制文件。
相关函数代码与说明:
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
struct Person people[3] = {
{"Alice", 25, 160.5},
{"Bob", 30, 175.0},
{"Charlie", 22, 180.2}
};
// 打开用于写入数据的二进制文件,如果不存在则创建
FILE *file = fopen("struct_bin_testfile.bin", "wb"); // "wb" 表示以二进制写入方式打开文件
if (file == NULL) {
perror("无法打开二进制文件");
return 1;
}
// 将结构体数组写入文件
fwrite(people, sizeof(struct Person), 3, file);
// 关闭文件
fclose(file);
printf("数据已写入二进制文件。\n");
// 修改第一个结构体的数据
strcpy(people[0].name, "Alice Updated");
people[0].age = 26;
people[0].height = 161.0;
// 打开文件以追加方式写入数据
file = fopen("people.bin", "ab"); // "ab" 表示以二进制追加方式打开文件
if (file == NULL) {
perror("无法打开二进制文件");
return 1;
}
// 将修改后的结构体写入文件的指定位置(这里写入第一个结构体的位置)
fseek(file, sizeof(struct Person) * 0, SEEK_SET);
fwrite(&people[0], sizeof(struct Person), 1, file);
// 关闭文件
fclose(file);
printf("第一个结构体已修改并重新写入二进制文件。\n");
return 0;
}
3.2.2读操作
从二进制文件读入结构体数组,数组长度未知。
相关函数代码与说明:在数组长度未知时,可以申请动态空间存储。本例中假设开始数组个数为10,在检测到多于十个时,将数组长度扩充至2倍,更新结构体数组指针,直至读取完毕(检查fread返回值实现)。读取后打印二进制文本,结果正确。最后要记得关闭文件和释放动态内存
#include <stdio.h>
#include <stdlib.h>
typedef struct Person
{
char name[50];
int age;
float height;
} PERSON;
int main()
{
FILE *file = fopen("struct_bin_testfile.bin", "rb"); // "rb" 表示以二进制读取方式打开文件
if (file == NULL)
{
perror("无法打开二进制文件");
return 1;
}
size_t size = 10;
PERSON *person = (PERSON *)malloc(sizeof(PERSON) * size);
// 动态申请内存来存储结构体数据
if (person == NULL)
{
perror("无法分配内存");
return 1;
}
size_t number = 0; // 数量
while (fread(&person[number], sizeof(PERSON), 1, file) == 1) // 使用 fread 从文件中读取数据并将其存储在动态分配的内存中
{
number++;
// 检查数组是否需要扩展
if (number >= size)
{
size = 2 * size;
PERSON *temp = (PERSON *)realloc(person, size * sizeof(PERSON));
if (temp == NULL)
{
perror("内存分配失败");
fclose(file);
free(person);
exit(EXIT_FAILURE);
}
person = temp;
}
}
fclose(file);
for (size_t i = 0; i < number; i++)
{
printf("name: %s ,age: %d, height:%.2f \n", person[i].name, person[i].age, person[i].height);
}
free(person);
return 0;
}
第4章 UNIX IO
4.1 UNIX IO的优缺点
-
-
优点 Unix I/O 是最通用、开销最低的I/O方式 所有其他 I/O都是使用Unix I/O 函数来实现的 Unix I/O 提供访问文件元数据的函数 Unix I/O 函数是异步信号安全的,可以在信号处理程序中安全地使用 缺点 处理不足值时容易出错 有效地读取文本行需要某种形式的缓冲, 容易出错 这两个问题都是由标准I/O和RIO包来解决
-
对比:标准IO
优点:
通过减少读和写系统调用的次数,有效增加内存
自动处理不足值
缺点:
没有提供访问文件元数据的函数
标准 I/O 函数不是异步信号安全的, 不适合用于信号处理
标准 I/O 不适合网络套接字的输入输出操作
对流的限制和对套接字的限制有时候会互相冲突,而又很少有文档描述这些现象
4.2 使用UNIX IO 完成实验
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "test.h"
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("input wrong!");
return 1;
}//there are numbers of input on screen in argc
int operation = atoi(argv[1]);//sring to number
const char *inputFileName = argv[2];
const char *outputFileName = argv[3];
if (operation == 1) {
textToScreenAndBinary(inputFileName, outputFileName);
} else if (operation == 2) {
binaryToScreenAndText(inputFileName, outputFileName);
} else {
printf("wrong operation! input '1' for text to binary or '2' for binary to text.\n");
return 1;
}
return 0;
}
main函数主要完成:读取命令行参数,检验合法性,并调用对应的函数
其中,atoi函数可以将命令行的字符串数字转为整型变量
// defination of struct
typedef struct myrecord
{
unsigned long id;
char name[32];
float score[4];
}record;
//input:2 file pointer
//output:null
//fuction:make text print to screen and change into bin
void textToScreenAndBinary(const char *inputFileName, const char *outputFileName)
{
int inputFile = open(inputFileName, O_RDONLY);//0_RDONLY means read only
if (inputFile == -1)
{
perror("Error opening input text file");
exit(1);
}
int outputFile = open(outputFileName,O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);//open bin file
if(outputFile == -1)
{
perror("Error opening output bin file\n");
exit(1);
}
// 计算行数
off_t len = lseek(inputFile,0,SEEK_END);//get length of txt
char* buf = malloc (len+1);
lseek(inputFile,0,SEEK_SET);//reset seek in 0
size_t st=read(inputFile,(void *)buf,len);//read txt in buf
if(st == -1)
{
perror("read fail!\n");
exit(1);
}
int num=0;//get the number of'\n'
for(int i=0;i<len;i++)
{
if(buf[i]=='\n') num++;
}
record *stu = (record*)malloc(sizeof(record)*(num+1));//malloc space for struct
char temp[128];int j=0,k=0;
for(int i=0;i<len&&(k<(num-1));i++)
{
if(buf[i]=='\n')//test '/n'
{
memcpy(temp,buf+j,i-j+1);//copy one line
sscanf(temp, "%lu %31s %f %f %f %f", &stu[k].id, stu[k].name, &stu[k].score[0], &stu[k].score[1], &stu[k].score[2], &stu[k].score[3]);//transport num from temp to struct
printf(" %lu %s %.1f %.1f %.1f %.1f\n", stu[k].id, stu[k].name, stu[k].score[0], stu[k].score[1], stu[k].score[2], stu[k].score[3]);//print
j=i+1;
k++;
}
}
if(write(outputFile,stu,sizeof(record)*(num-1))<0)//write
{
perror("write fail!\n");
exit(1);
}
close(outputFile);
close(inputFile);
free(buf);
free(stu);//close file and free memery
}
//input:2 file pointers
//output:null
//function:bin file print to screen and change to struct txt
void binaryToScreenAndText(const char *inputFileName, const char *outputFileName) {
int inputFile = open(inputFileName, O_RDONLY);//read only
if (inputFile == -1) {
perror("Error opening input binary file");
exit(1);
}
int outputFile = open(outputFileName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (outputFile == -1)
{
perror("Error opening output text file");
close(inputFile);
exit(1);
}
struct myrecord record;
while (1) {
ssize_t bytesRead = read(inputFile, &record, sizeof(struct myrecord));//bytesread means the return number of read function
if (bytesRead == 0) {
break; // 文件读取结束
} else if (bytesRead == -1) //read wrong
{
perror("Error reading input binary file");
close(inputFile);
close(outputFile);
exit(1);
}
printf("%lu, %s, %.2f, %.2f, %.2f, %.2f\n", record.id, record.name, record.score[0], record.score[1], record.score[2], record.score[3]);
char line[128];
snprintf(line, sizeof(line), "%lu %s %.2f %.2f %.2f %.2f\n", record.id, record.name, record.score[0], record.score[1], record.score[2], record.score[3]);//sizeof(line) limit the length of write,avioding overflow
if (write(outputFile, line, strlen(line)) == -1)//write wrong
{
perror("Error writing output text file");
close(inputFile);
close(outputFile);
exit(1);
}
}
close(inputFile);
close(outputFile);
}
open()是一个常用的C语言标准库函数,用于打开文件并返回一个文件描述符。
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
其返回值是一个指示文件的整型变量,负数表示打开失败。
参数说明:
pathname表示文件路径。
flags标志位,表示一系列对文件操作的权限,如O_RDONLY表示只读,O_WRONLY表示只写, O_CREAT表示没有该文件时创建, O_TRUNC表示如果文件已经存在,那么在打开文件时将截断(清空)文件内容,即将文件的大小截断为零字节, S_IRUSR表示文件的所有者有读取权限,S_IWUSR表示用户具有对文件或目录的写权限。
Linux 文件IO学习之open函数深入了解_linux open函数_catfish coffee的博客-CSDN博客
off_t len = lseek(inputFile,0,SEEK_END)
这段代码用来获取文件的大小。off_t是一个用于文件大小偏移的类型,它是一个有符号整数类型。
lseek函数原型如下
off_t lseek(int fildes, off_t offset, int whence);
fildes指示文件,offset表示偏移开头的字符数,置为0则是从文件起始读取。whence表示读到哪,SEEK_END表示读到文件结尾。返回读取的字符数。
注意到这行代码
lseek(inputFile,0,SEEK_SET);//reset seek in 0
为什么要求文件描述符回到文件开头呢?这和read函数的定义有关。
ssize_t read(int fd, void *buf, size_t count);
参数说明:
fd:一个打开的文件描述符。
buf:指向一个缓冲区的指针,用于存储读取到的数据。
count:要读取的字节数。
返回值是实际读取到的字节数,如果文件已经到达结尾,则返回0,如果出现错误,则返回-1。
memcpy()
是一个C语言标准库函数,用于从源内存区域复制n个字节到目标内存区域。
void *memcpy(void *dest, const void *src, size_t n);
memcpy()函数会返回一个指向目标内存区域的指针。
参数说明:
dest:目标内存区域的指针。
src:源内存区域的指针。
n:要复制的字节数。
write()
是一个Unix系统调用,用于向打开的文件描述符写入数据。
ssize_t write(int fd, const void *buf, size_t count);
参数说明:
fd:一个打开的文件描述符。
buf:指向一个缓冲区的指针,其中包含了要写入的数据。
count:要写入的字节数。
返回值是实际写入的字节数,如果出现错误,则返回-1。