文章目录
- 1. Linux开发环境搭建
- 2. GCC
- 2.1 什么是GCC ?
- 2.2 GCC工作流程 / C++从代码到可执行文件的过程
- 2.3 gcc 与 g++的区别
- 2.4 GCC常用参数选项
- 3. Makefile
- 3.1 简介
- 3.2 Makefile 文件命名和规则
- 3.3 基本原理
- 3.4 变量
- 3.5 模式匹配
- 3.6 函数
- 4. GDB
- 5. 静态库和动态库
- 5.1 什么是库?
- 5.2 静态库
- 5.3 动态库
- 5.4 工作原理
- 5.5 静态库与动态库的区别
- 6. 文件
- 6.1 标准 C 库 IO 函数
- 6.2 标准 C 库 IO 和 Linux 系统 IO 的关系
- 6.3 虚拟地址空间
- 6.4 文件描述符
- 6.5 Linux系统IO函数
- 6.6 stat 结构体
- 6.7 st_mode 变量
- 6.8 文件属性操作函数
- 6.9 目录操作函数
- 6.10 目录遍历函数
- 6.11 dirent 结构体和 d_type
- 6.12 dup、dup2 函数
- 6.13 fcnl 函数
https://releases.ubuntu.com/bionic/
https://www.netsarang.com/zh/free-for-home-school/
https://code.visualstudio.com/
sudo apt install openssh-server
sudo apt install build-essential
-std=c99
启动 GCC 时,编译器支持 C99 标准。sudo apt install gcc g++
(版本 > 4.8.5)gcc/g++ -v/--version
.i
结尾的文件名。.s
结尾的汇编代码。.o
结尾的目标代码。.out
或者.exe
结尾)。静态链接和动态链接
。
.lib
为后缀,Linux中以.a
为后缀。.dll
为后缀,Linux中以.so
为后缀。.c
的程序,gcc 把它当作是 C 程序,而 g++ 把它当作 c++ 程序。而后缀为.cpp
的程序,两者都会认为是 c++ 程序。-E
:预处理指定的源文件,但是不进行编译-S
:编译指定的源文件,但是不进行汇编-c
:编译、汇编指定的源文件,但是不进行链接[file2] -o [file1]
或 -o [file1] [file2]
:将文件 file2
编译成可执行文件 file1
-I directory
:指定 include 包含文件的搜索目录-g
:在编译的时候,生成调试信息,该程序可以被调试器调试-D
:在程序编译的时候,指定一个宏-w
:不生成任何警告信息-Wall
:生成所有的警告信息-On
:n的取值范围为0~3
。编译器的优化选项的4个级别,-O0 表示没有优化,- O1为缺省值,-O3优化级别最高-l
:在程序编译的时候,指定使用的库-L
:在程序编译的时候,指定搜索的库的路径-fPIC/fpic
:生成与位置无关的代码-shared
:生成共享目标文件,通常用在建立共享库时-std
:用来指定C方言,如:-std=c99
,gcc 默认的方言是 GNU C自动化编译
,一旦写好,只需要通过执行一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。make 是一个解释 Makefile 文件中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如 Visual C++ 的 nmake,Linux 下 GNU 的 make。目标... : 依赖...
命令(Shell 命令)
...
Makefile 中的其他规则一般都是为第一条规则服务的。变量名=变量值
,比如var=hello
,定义了一个变量var
,变量值为hello
。$(变量名)
,比如$(var)
,获取到var
变量的值hello
。AR
:归档维护程序的名称,默认值为ar
。CC
:C 编译器的名称,默认值为cc
。CXX
:C++ 编译器的名称,默认值为g++
。$@
:目标的完整名称。$<
:第一个依赖文件的名称。$^
:所有的依赖文件的名称。注意⚠️:自动变量只能在规则的命令中使用
app:main.c a.c b.c
gcc -c main.c a.c b.c -o app
等价于
app:main.c a.c b.c
$(CC) -c $^ -o $@
%.o:%.c
:该命令中的%
是通配符,匹配一个字符串,命令中的两个%
匹配的是相同的字符串。
app:sub.o add.o mult.o div.o main.o
gcc sub.o add.o mult.o div.o main.o -o app
add.o:add.c
gcc -c add.c -o add.o
div.o:div.c
gcc -c div.c -o div.o
sub.o:sub.c
gcc -c sub.c -o sub.o
mult.o:mult.c
gcc -c mult.c -o mult.o
main.o:main.c
gcc -c main.c -o main.o
等价于
src=sub.o add.o mult.o div.o main.o
target=app
$(target):$(src)
$(CC) $(src) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
(1)$(wildcard PATTERN...)
PATTERN
指的是一个或多个目录下的某种类型的文件,如果有多个目录,一般使用空格隔开$(wildcard ./*.c ./*.o)
,该命令获取当前目录下的所有以.c
和.o
结尾的文件,并以文件列表的形式返回。(2)$(patsubst
中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式
,如果匹配的话,则以
替换。
可以包括通配符%
,表示任意长度的字串。如果
中也包含%
,那么,
中的这个%
将是
中的那个%
所代表的字串。(可以用\
来转义,以\%
来表示真实含义的%
字符)$(patsubst %.c, %.o, x.c bar.c)
,返回值格式:x.o bar.o
注意⚠️:当生成目标文件时也生成了一些中间文件,但是这些中间文件最终不需要,因此需要将中间文件进行删除,例如,当生成可执行文件的过程中生成了以.o
结尾的文件a.o
和b.o
,可使用下列的规则对其删除:
.PHONY:clean
clean:
rm a.o b.o -f
-O
), 并打开调试选项(-g
)。另外,在尽量不影响程序行为的情况下选项打开所有warning(-Wall
),也可以发现许多问题,避免一些不必要的 BUG。
-g
选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证 gdb 能找到源文件。gdb 可执行程序
、quit
set args 10 20
、show args
help
list/l
(从默认位置显示)、list/l 行号
(从指定的行显示)、list/l 函数名
(从指定的函数显示)list/l 文件名:行号
、list/l 文件名:函数名
show list/listsize
、set list/listsize 行数
b/break 行号
、b/break 函数名
、b/break 文件名:行号
、b/break 文件名:函数
i/info b/break
d/del/delete 断点编号
dis/disable 断点编号
ena/enable 断点编号
b/break 10 if i==5
start
(程序停在第一行)、run
(遇到断点才停)c/continue
n/next
p/print 变量名
(打印变量值)、ptype 变量名
(打印变量类型)s/step
、finish(跳出函数体)
display 变量名
(自动打印指定变量的值)、i/info display
、undisplay 编号
set var 变量名=变量值
(循环中用的较多)、until
(跳出循环)(1)命名规则
libxxx.a
,其中:lib
为固定的前缀,xxx
为库的名称(自己取),.a
为固定的后缀。libxxx.lib
。(2)静态库的制作
gcc -c xxx
获得 .o
文件;.o
文件打包,使用ar
工具(archive)生成静态库。ar rcs libxxx.a xxx.o xxx.o
对于rcs
,r
表示将文件插入备存文件中,c
表示建立备存文件,s
表示索引。
(3)通过静态库生成可执行文件
src
目录下执行以下命令gcc main.c -o app -I ../include/ -l calc -L ../lib/
在当前src
目录下生成可执行文件app
,直接执行./app
即可运行程序。(1)命名规则
libxxx.so
,其中:lib
为固定的前缀,xxx
为库的名称(自己取),.so
为固定的后缀。libxxx.dll
。(2)动态库的制作
gcc
得到.o
文件,得到和位置无关的代码gcc -c -fpic/-fPIC a.c b.c
gcc
得到动态库。gcc -shared a.o b.o -o libcalc.so
(3)通过动态库生成可执行文件
src
目录下执行以下命令gcc main.c -o app -I ../include/ -l calc -L ../lib/
在当前src
目录下生成可执行文件app
,但是此时执行./app
命令却出现错误,错误为error while loading shared libraries: lobcalc.so
。原因就是此时的可执行文件无法定位共享库文件的位置。程序启动以后,动态库会被动态加载到内存中,通过 ldd (list dynamic dependencies)
命令检查动态库依赖关系,当系统加载可执行代码的时候,能够知道其所依赖的库的名字,但是还需要知道库的绝对路径。此时就需要系统的动态载入器来获取该绝对路径。对于 elf 格式的可执行程序,是由 ld-linux.so 来完成的,它先后搜索 elf 文件的 DT_RPATH 段——>环境变量LD_LIBRARY_PATH——> /etc/ld.so.cache 文件列表——> /lib/,/usr/lib目录找到库文件后将其载入内存,通过./app
即可运行程序。LD_LIBRARY_PATH
,具体实现为:~/.bashrc
文件中。.bashrc
文件末尾添加命令export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wzp/Linux/lesson03/04_lib/library/lib
,修改完~/.bashrc
文件后,记得执行source ~/.bashrc
或. .bashrc
,来将修改应用到当前的bash环境下。ldd (list dynamic dependencies)
命令检查动态库依赖关系。(1)int open(const char *pathname, int flags)
函数描述:主要作用是打开一个文件用于读或写
#include
#include
#include
// 打开一个已经存在的文件
int open(const char *pathname, int flags);
参数:
- pathname:要打开的文件路径
- flags:对文件的操作权限设置还有其他的设置
O_RDONLY, O_WRONLY, O_RDWR 这三个设置是互斥的
返回值:返回一个新的文件描述符,如果调用失败,返回-1
errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。
#include
void perror(const char *s);作用:打印errno对应的错误描述
s参数:用户描述,比如hello,最终输出的内容是 hello:xxx(实际的错误描述)
样例:
#include
#include
#include
#include
#include
int main() {
// 打开一个文件
int fd = open("a.txt", O_RDONLY); //打开当前路径下的 a.txt 文件
if(fd == -1) {
perror("open");
}
// 读操作
// 关闭
close(fd);
return 0;
}
(2)int open(const char *pathname, int flags, ,mode_t mode)
函数描述:主要作用是创建一个文件用于读或写
#include
#include
#include
int open(const char *pathname, int flags, mode_t mode);
参数:
- pathname:要创建的文件的路径
- flags:对文件的操作权限和其他的设置
- 必选项:O_RDONLY, O_WRONLY, O_RDWR 这三个之间是互斥的
- 可选项:O_CREAT 当文件不存在时,创建新文件
- mode:八进制的数,表示创建出的新的文件的操作权限,比如:0775
最终的权限是:mode & ~umask
0777 -> 111111111
& 0775 -> 111111101
----------------------------
111111101
按位与:0和任何数都为0
umask的作用就是抹去某些权限。
flags参数是一个int类型的数据,占4个字节,32位。
flags 32个位,每一位就是一个标志位。
样例:
#include
#include
#include
#include
#include
int main() {
// 在当前路径下创建一个新的文件
int fd = open("create.txt", O_RDWR | O_CREAT, 0777);
if(fd == -1) {
perror("open");
}
// 关闭
close(fd);
return 0;
}
(3)int close(int fd)
函数描述:关闭一个文件描述符
(4)ssize_t read(int fd, void *buf, size_t count)
函数描述:read()函数从文件描述符fd指向的文件对象中读取数据,读取的数据大小为count字节,将读取的数据存放到buf指向的缓冲区中。
#include
ssize_t read(int fd, void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:需要读取数据存放的地方,数组的地址(传出参数)
- count:指定的数组的大小
返回值:
- 成功:
>0: 返回实际的读取到的字节数
=0:文件已经读取完了
- 失败:-1 ,并且设置errno
(5)ssize_t write(int fd, const void *buf, size_t count)
函数描述:write()函数对文件描述符fd指向的文件对象中写入数据,要写的数据大小为count字节,将要写入磁盘的数据存放到buf指向的缓冲区中。
#include
ssize_t write(int fd, const void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:要往磁盘写入的数据,数据
- count:要写的数据的实际的大小
返回值:
- 成功:实际写入的字节数
- 失败:返回-1,并设置errno
样例:
#include
#include
#include
#include
#include
int main() {
// 1.通过open打开english.txt文件
int srcfd = open("english.txt", O_RDONLY);
if(srcfd == -1) {
perror("open");
return -1;
}
// 2.创建一个新的文件(拷贝文件)
int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
if(destfd == -1) {
perror("open");
return -1;
}
// 3.频繁的读写操作
char buf[1024] = {0};
int len = 0;
while((len = read(srcfd, buf, sizeof(buf))) > 0) {
write(destfd, buf, len);
}
// 4.关闭文件描述符
close(destfd);
close(srcfd);
return 0;
}
(6)off_t lseek(int fd, off_t offset, int whence)
函数描述:lseek()函数主要对文件指针进行操作,完成相应的功能。
标准C库的函数
#include
int fseek(FILE *stream, long offset, int whence);
Linux系统函数
#include
#include
off_t lseek(int fd, off_t offset, int whence);
参数:
- fd:文件描述符,通过open得到的,通过这个fd操作某个文件
- offset:偏移量
- whence:
SEEK_SET
设置文件指针的偏移量
SEEK_CUR
设置偏移量:当前位置 + 第二个参数offset的值
SEEK_END
设置偏移量:文件大小 + 第二个参数offset的值
返回值:返回文件指针的位置
作用:
1.移动文件指针到文件头
lseek(fd, 0, SEEK_SET);
2.获取当前文件指针的位置
lseek(fd, 0, SEEK_CUR);
3.获取文件长度
lseek(fd, 0, SEEK_END);
4.拓展文件的长度,当前文件10b, 110b, 增加了100个字节
lseek(fd, 100, SEEK_END)
注意:需要写一次数据
样例:
#include
#include
#include
#include
#include
int main() {
int fd = open("hello.txt", O_RDWR);
if(fd == -1) {
perror("open");
return -1;
}
// 扩展文件的长度
int ret = lseek(fd, 100, SEEK_END);
if(ret == -1) {
perror("lseek");
return -1;
}
// 写入一个空数据
write(fd, " ", 1);
// 关闭文件
close(fd);
return 0;
}
(7)int stat(const char *pathname, struct stat *statbuf)
函数描述:stat()函数获取pathname所指向的文件的一些信息。
#include
#include
#include
int stat(const char *pathname, struct stat *statbuf);
作用:获取一个文件相关的一些信息
参数:
- pathname:操作的文件的路径
- statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
返回值:
成功:返回0
失败:返回-1 设置errno
样例:
#include
#include
#include
#include
int main() {
struct stat statbuf;
int ret = stat("a.txt", &statbuf);
if(ret == -1) {
perror("stat");
return -1;
}
printf("size: %ld\n", statbuf.st_size);
return 0;
}
(8)int lstat(const char *pathname, struct stat *statbuf)
作用:lstat()函数与stat()类似,只是命名文件是符号链接的情况不同:
lstat()返回的是该符号链接本身的信息,而stat()返回该链接链接指向的文件的信息。
参数:
- pathname:操作的文件的路径
- statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
返回值:
- 成功:返回0
- 失败:返回-1 设置errno
struct stat {
dev_t st_dev; // 文件的设备编号
ino_t st_ino; // 节点
mode_t st_mode; // 文件的类型和存取的权限
nlink_t st_nlink; // 连到该文件的硬连接数目
uid_t st_uid; // 用户ID
gid_t st_gid; // 组ID
dev_t st_rdev; // 设备文件的设备编号
off_t st_size; // 文件字节数(文件大小)
blksize_t st_blksize; // 块大小
blkcnt_t st_blocks; // 块数
time_t st_atime; // 最后一次访问时间
time_t st_mtime; // 最后一次修改时间
time_t st_ctime; // 最后一次改变时间(指属性)
};
man 2 funcname
指令查询Linux相关函数的详细描述。int access(const char *pathname, int mode)
#include
int access(const char *pathname, int mode);
作用:判断某个文件是否有某个权限,或者判断文件是否存在
参数:
- pathname: 判断的文件路径
- mode:
R_OK: 判断是否有读权限
W_OK: 判断是否有写权限
X_OK: 判断是否有执行权限
F_OK: 判断文件是否存在
返回值:成功返回0, 失败返回-1
样例:
#include
#include
int main() {
int ret = access("a.txt", F_OK);
if(ret == -1) {
perror("access");
}
printf("文件存在!!!\n");
return 0;
}
(2)int chmod(const char *filename, int mode)
#include
int chmod(const char *pathname, mode_t mode);
作用:修改文件的权限
参数:
- pathname: 需要修改的文件的路径
- mode:需要修改的权限值,八进制的数
返回值:成功返回0,失败返回-1
样例:
#include
#include
int main()
{
int ret = chmod("a.txt", 0775);
if (ret == -1)
{
perror("chmod");
return -1;
}
return 0;
}
(3)int chown(const char *path, uid_t owner, gid_t group)
修改文件的所有者id和所在组id,通过vim /etc/passwd
可查看用户id和所在组的id,通过vim /etc/group
可查看所有组的id,通过id 用户名
可得到用户的id、组id。
(4)int truncate(const char *path, off_t length)
#include
#include
int truncate(const char *path, off_t length);
作用:缩减或者扩展文件的尺寸至指定的大小
参数:
- path: 需要修改的文件的路径
- length: 需要最终文件变成的大小
返回值:
成功返回0, 失败返回-1
样例:
#include
#include
#include
int main() {
int ret = truncate("b.txt", 5);
if(ret == -1) {
perror("truncate");
return -1;
}
return 0;
}
(1)int rename(const char *oldpath, const char *newpath)
样例:
#include
int main() {
int ret = rename("aaa", "bbb");
if(ret == -1) {
perror("rename");
return -1;
}
return 0;
}
(2)int chdir(const char *path)
#include
int chdir(const char *path);
作用:修改进程的工作目录
比如在/home/nowcoder 启动了一个可执行程序a.out, 进程的工作目录 /home/nowcoder
参数:
path : 需要修改的工作目录
#include
char *getcwd(char *buf, size_t size);
作用:获取当前工作目录
参数:
- buf : 存储的路径,指向的是一个数组(传出参数)
- size: 数组的大小
返回值:
返回的指向的一块内存,这个数据就是第一个参数
样例:
#include
#include
#include
#include
#include
int main() {
// 获取当前的工作目录
char buf[128];
getcwd(buf, sizeof(buf));
printf("当前的工作目录是:%s\n", buf);
// 修改工作目录
int ret = chdir("/home/nowcoder/Linux/lesson13");
if(ret == -1) {
perror("chdir");
return -1;
}
// 创建一个新的文件
int fd = open("chdir.txt", O_CREAT | O_RDWR, 0664);
if(fd == -1) {
perror("open");
return -1;
}
close(fd);
// 获取当前的工作目录
char buf1[128];
getcwd(buf1, sizeof(buf1));
printf("当前的工作目录是:%s\n", buf1);
return 0;
}
(3)char *getcwd(char *buf, size_t size)
(4)int mkdir(const char *pathname, mode_t mode)
#include
#include
int mkdir(const char *pathname, mode_t mode);
作用:创建一个目录
参数:
pathname: 创建的目录的路径
mode: 权限,八进制的数
返回值:
成功返回0, 失败返回-1
样例:
#include
#include
#include
int main() {
int ret = mkdir("aaa", 0777);
if(ret == -1) {
perror("mkdir");
return -1;
}
return 0;
}
(5)int rmdir(const char *pathname)
(1)DIR *opendir(const char *name)
// 打开一个目录
#include
#include
DIR *opendir(const char *name);
参数:
- name: 需要打开的目录的名称
返回值:
DIR * 类型,理解为目录流
错误返回NULL
(2)struct dirent *readdir(DIR *dirp)
// 读取目录中的数据
#include
struct dirent *readdir(DIR *dirp);
- 参数:dirp是opendir返回的结果
- 返回值:
struct dirent,代表读取到的文件的信息
读取到了末尾或者失败了,返回NULL
(3)int closedir(DIR *dirp)
// 关闭目录
#include
#include
int closedir(DIR *dirp);
样例:
#include
#include
#include
#include
#include
int getFileNum(const char * path);
// 读取某个目录下所有的普通文件的个数
int main(int argc, char * argv[]) {
if(argc < 2) {
printf("%s path\n", argv[0]);
return -1;
}
int num = getFileNum(argv[1]);
printf("普通文件的个数为:%d\n", num);
return 0;
}
// 用于获取目录下所有普通文件的个数
int getFileNum(const char * path) {
// 1.打开目录
DIR * dir = opendir(path);
if(dir == NULL) {
perror("opendir");
exit(0);
}
struct dirent *ptr;
// 记录普通文件的个数
int total = 0;
while((ptr = readdir(dir)) != NULL) {
// 获取名称
char * dname = ptr->d_name;
// 忽略掉. 和..
if(strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) {
continue;
}
// 判断是否是普通文件还是目录
if(ptr->d_type == DT_DIR) {
// 目录,需要继续读取这个目录
char newpath[256];
sprintf(newpath, "%s/%s", path, dname);
total += getFileNum(newpath);
}
if(ptr->d_type == DT_REG) {
// 普通文件
total++;
}
}
// 关闭目录
closedir(dir);
return total;
}
(1)int dup(int oldfd)
#include
int dup(int oldfd);
作用:复制一个新的文件描述符
fd=3, int fd1 = dup(fd),
fd指向的是a.txt, fd1也是指向a.txt
从空闲的文件描述符表中找一个最小的,作为新的拷贝的文件描述符
样例:
#include
#include
#include
#include
#include
#include
int main() {
int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
int fd1 = dup(fd);
if(fd1 == -1) {
perror("dup");
return -1;
}
printf("fd : %d , fd1 : %d\n", fd, fd1);
close(fd);
char * str = "hello,world";
int ret = write(fd1, str, strlen(str));
if(ret == -1) {
perror("write");
return -1;
}
close(fd1);
return 0;
}
(2)int dup2(int oldfd, int newfd)
#include
int dup2(int oldfd, int newfd);
作用:重定向文件描述符
oldfd 指向 a.txt, newfd 指向 b.txt
调用函数成功后:newfd 和 b.txt 做close, newfd 指向了 a.txt
oldfd 必须是一个有效的文件描述符
oldfd和newfd值相同,相当于什么都没有做
样例:
#include
#include
#include
#include
#include
#include
int main() {
int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
if(fd == -1) {
perror("open");
return -1;
}
int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
if(fd1 == -1) {
perror("open");
return -1;
}
printf("fd : %d, fd1 : %d\n", fd, fd1);
int fd2 = dup2(fd, fd1);
if(fd2 == -1) {
perror("dup2");
return -1;
}
// 通过fd1去写数据,实际操作的是1.txt,而不是2.txt
char * str = "hello, dup2";
int len = write(fd1, str, strlen(str));
if(len == -1) {
perror("write");
return -1;
}
printf("fd : %d, fd1 : %d, fd2 : %d\n", fd, fd1, fd2);
close(fd);
close(fd1);
return 0;
}
int fcntl(int fd, int cmd, ... /* arg */ )
:复制文件描述符、设置/获取文件的状态标志
#include
#include
int fcntl(int fd, int cmd, ...);
参数:
fd : 表示需要操作的文件描述符
cmd: 表示对文件描述符进行如何操作
- F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
int ret = fcntl(fd, F_DUPFD);
- F_GETFL : 获取指定的文件描述符文件状态flag
获取的flag和我们通过open函数传递的flag是一个东西。
- F_SETFL : 设置文件描述符文件状态flag
必选项:O_RDONLY, O_WRONLY, O_RDWR 不可以被修改
可选性:O_APPEND, O)NONBLOCK
O_APPEND 表示追加数据
NONBLOK 设置成非阻塞
阻塞和非阻塞:描述的是函数调用的行为。
样例:
#include
#include
#include
#include
int main() {
// 1.复制文件描述符
// int fd = open("1.txt", O_RDONLY);
// int ret = fcntl(fd, F_DUPFD);
// 2.修改或者获取文件状态flag
int fd = open("1.txt", O_RDWR);
if(fd == -1) {
perror("open");
return -1;
}
// 获取文件描述符状态flag
int flag = fcntl(fd, F_GETFL);
if(flag == -1) {
perror("fcntl");
return -1;
}
flag |= O_APPEND; // flag = flag | O_APPEND
// 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
int ret = fcntl(fd, F_SETFL, flag);
if(ret == -1) {
perror("fcntl");
return -1;
}
char * str = "nihao";
write(fd, str, strlen(str));
close(fd);
return 0;
}