当前位置: 首页 > news >正文

02. Linux嵌入式系统学习笔记(二)(编写C程序进行文件操作、模块化编程makefile的使用)

目录

一.使用系统低级文件读写访问接口进行文件的读写练习。

1. 创建 test.txt 文件

2. 编写 modify.c 程序

(0). 头文件

(1). open函数

(2). read函数

(3). write函数 

(4). close函数 

(5). rename函数

3. 编译并运行程序

二. 编写一个计算 5+(9/3)的程序

1. 创建目录并进入

2. 创建头文件 myhead.h

3. 创建加法实现文件 myadd.c

4. 创建除法实现文件 mydiv.c

5. 创建主程序文件 result.c

6. 创建 Makefile 文件

7. 项目运行


一.使用系统低级文件读写访问接口进行文件的读写练习。

要求:

1. 创建一个文件 test.txt,文件内容为:

2. 编写一个 c 程序 modify.c,读写这个文件,修改其内容,添加一行,将文件内容变成。

提示:可以把正确的内容写入一个临时文件,然后把临时文件重命名。

1. 创建 test.txt 文件

首先,创建一个名为 test.txt 的文件,并写入以下内容:

echo -e "1\n2\n3\n5" > test.txt

2. 编写 modify.c 程序

        接下来,编写一个C程序 modify.c,该程序将读取 test.txt 文件的内容,修改内容,并将修改后的内容写入一个临时文件,最后将临时文件重命名为 test.txt。 

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define TEMP_FILE "temp.txt"
#define BUF_SIZE 1024

int main()
{
	int fd_input, fd_output;
	char buffer[BUF_SIZE];
	ssize_t bytes_read, bytes_written;
	char *target_char = "3"; // 要查找的字符
	// 打开原始文件
	fd_input = open("test.txt", O_RDONLY);	// 只读模式打开
	if(fd_input == 1)
	{
		perror("Error opening test.txt\n");
		return 1;
	}

	// 创建临时文件
	fd_output = open(TEMP_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);// 只写模式打开文件|创建文件并清空文件
	if(fd_output == -1)
	{
		perror("Error creating temp file\n");
		close(fd_input);
		return 1;
	}

	// 读取原始文件内容并写入临时文件
	bytes_read = read(fd_input,buffer, sizeof(buffer));
	if(bytes_read == -1)
	{
		perror("Error reading from test.txt\n");
		close(fd_input);
		close(fd_output);
		return 1;
	}


	// 在‘5’前面添加‘4’
	for(int i = bytes_read - 1;i >= 0; i--)
	{
		buffer[i + 3] = buffer[i];	
		if(buffer[i] == '5')
		{
			buffer[i] = '4';
			buffer[i+1] = '\n';
			buffer[i+2] = '\n';
			break;
		}
	}

	buffer[strlen(buffer)] = '\0'; 	// 添加字符串结束符

	// 将原文件内容拷贝到临时文件中
	bytes_written = write(fd_output, buffer, strlen(buffer));

	// 关闭文件描述符
	close(fd_input);
	close(fd_output);

	// 重命名临时文件名为源文件名
	if(rename(TEMP_FILE, "test1.txt") == -1)
	{
		perror("Error renaming temp file\n");
		return 1;
	}

	printf("File modified successfully.\n");

	return 0;
}

(0). 头文件

头文件功能描述在本程序中的作用
<stdio.h>提供标准输入输出函数,如 printf 和 perror使用 perror 打印错误信息。
<stdlib.h>

提供通用工具函数,如内存管理(malloc、free)和程序控制(exit)。

未直接使用,但通常包含在C程序中。
<fcntl.h>

提供文件控制选项的常量定义,如 

O_RDONLY (只读打开)、

 O_WRONLY(只写打开)、

O_CREAT(若文件不存在则创建文件)、

O_TRUNC(若文件存在则将其长度截断为0)、

定义 open 系统调用中使用的标志。
<unistd.h>

提供POSIX操作系统API,如 

open:打开文件。

read:从文件读取数据。

write:向文件写入数据。

close:关闭文件描述符。

rename:重命名文件。

使用这些系统调用来操作文件。
<string.h>

提供字符串操作函数,如 

strlen:计算字符串的长度。

strcpy:复制字符串。

strcmp:比较字符串。

使用 strlen 计算要写入的新行 "4\n" 的长度。

(1). open函数

功能:打开或创建一个文件,并返回文件描述符

函数原型:#include <fcntl.h

int open(const char *pathname, int flags, mode_t mode);
参数

pathname

要打开或创建的文件路径。

flags

打开文件的标志,指定文件的访问模式和行为。常用标志:

O_RDONLY:只读模式。

O_WRONLY:只写模式。

O_RDWR:读写模式。

O_CREAT:如果文件不存在,则创建文件。

O_TRUNC:如果文件存在且为只写或读写模式,则将其长度截断为0。

O_APPEND:在文件末尾追加数据。

mode

创建文件时指定文件的权限(仅在 O_CREAT 标志时有效)。权限通常用八进制表示,如 0644

返回值:​​​​​​

成功:返回文件描述符(一个非负整数)。

失败:返回 -1,并设置 errno

(2). read函数

功能:从文件描述符对应的文件中读取数据。

函数原型:#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
参数

fd

文件描述符。

buf

存储读取数据的缓冲区。

count

要读取的字节数。

返回值:​​​​​​

成功:返回实际读取的字节数(可能小于 count)。

失败:返回 -1,并设置 errno

文件结束:返回 0

(3). write函数 

功能:向文件描述符对应的文件中写入数据。

函数原型:#include <unistd.h>


ssize_t write(int fd, const void *buf, size_t count);
参数

fd

文件描述符。

buf

存储读取数据的缓冲区。

count

要读取的字节数。

返回值

成功:返回实际写入的字节数(可能小于 count)。

失败:返回 -1,并设置 errno

 (4). lseek函数 (调整文件的偏移量-文件指针的位置)

功能:调整文件的偏移量。

函数原型:#include <unistd.h>

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

fd

文件描述符。lseek调用后,文件描述符随之而改变
offset偏移量。
whence:基准位置,可以是

SEEK_SET:从文件开头开始计算。

SEEK_CUR:从当前偏移量开始计算。

SEEK_END:从文件末尾开始计算。

返回值

成功:返回新的偏移量。

失败:返回 -1,并设置 errno

注意O_APPEND 标志:如果文件是以 O_APPEND 标志打开的,write 会始终将数据追加到文件末尾,忽略 lseek 设置的偏移量。

lseek的使用 

场景说明
配合 read 使用调整文件的读取位置。
配合 write 使用调整文件的写入位置。
获取当前偏移量使用 lseek(fd, 0, SEEK_CUR)
获取文件大小使用 lseek(fd, 0, SEEK_END)。

(4). close函数 

功能:关闭文件描述符,释放相关资源。

函数原型:#include <unistd.h>


int close(int fd);
参数

fd

要关闭的文件描述符。

返回值

成功:返回 0

失败:返回 -1,并设置 errno

(5). rename函数

功能:打开或创建一个文件,并返回文件描述符

函数原型:#include <stdio.h>


int rename(const char *oldpath, const char *newpath);
参数

oldpath

原文件或目录的路径。

newpath

新文件或目录的路径。

返回值

成功:返回 0

失败:返回 -1,并设置 errno

3. 编译并运行程序

编译 modify.c 并运行它:

gcc modify.c -o modify
./modify

二. 编写一个计算 5+(9/3)的程序

        要求:分成 4 个文件,一个头文件 myhead.h,一个进行加法运算的 myadd.c 代码文件、一个进行除法的文件 mydiv.c 和一个reslut.c 文件。编写一个 Makefile,使它们在 make 工具下生成可执行文件myresult。整个项目文件都放在一个目录 cal 下面

1. 创建目录并进入

mkdir -p cal && cd cal
  • mkdir -p

    • mkdir:创建目录的命令(make directory)

    • -p 参数:自动创建父目录(如果父目录不存在),但在此例中cal是直接创建的,所以-p的作用是防止因目录已存在而报错。

  • &&:逻辑运算符,表示前一条命令成功执行后,再执行后面的命令。

  • cd cal:进入新创建的 cal 目录。

2. 创建头文件 myhead.h

#ifndef MYHEAD_H
#define MYHEAD_H

int myadd(int a, int b);
int mydiv(int a, int b);

#endif
  • 作用

    • 头文件保护:#ifndef 和 #endif 防止同一个头文件被多次包含(避免重复定义错误)。

    • 声明函数:告诉编译器 myadd 和 mydiv 的存在,具体实现在 .c 文件中。

3. 创建加法实现文件 myadd.c

int myadd(int a, int b) {
    return a + b;
}
  • 关键点

    • 函数名和参数必须与头文件 myhead.h 中的声明完全一致。

4. 创建除法实现文件 mydiv.c

int mydiv(int a, int b) {
    return a / b;
}
  • 注意

    • 如果 b=0 会导致除零错误,但此例中 b=3 是安全的。

5. 创建主程序文件 result.c

#include <stdio.h>
#include "myhead.h"

int main() {
    int a = 5;
    int b = mydiv(9, 3);
    int result = myadd(a, b);
    printf("Result: %d\n", result);
    return 0;
}

6. 创建 Makefile 文件

CC = gcc
CFLAGS = -I.

all: myresult

myresult: myadd.o mydiv.o result.o
	$(CC) -o $@ $^ $(CFLAGS)

myadd.o: myadd.c myhead.h
	$(CC) -c $< $(CFLAGS)

mydiv.o: mydiv.c myhead.h
	$(CC) -c $< $(CFLAGS)

result.o: result.c myhead.h
	$(CC) -c $< $(CFLAGS)

clean:
	rm -f *.o myresult

Makefile 关键语法

  • 变量CC 和 CFLAGS 是自定义变量,简化命令的重复输入。

  • 目标依赖

    • myresult: myadd.o mydiv.o result.o 表示生成 myresult 需要依赖三个 .o 文件。

    • 如果某个 .o 文件不存在或对应的 .c 文件被修改,Makefile 会自动重新编译。

  • 自动变量

    • $@:当前目标名(如 myresult)。

    • $^:所有依赖文件(如 myadd.o mydiv.o result.o)。

    • $<:第一个依赖文件(如 myadd.c)。

  • clean 规则:用于清理生成的文件,需手动执行 make clean

  • -I. 的作用是什么?
    告诉编译器在当前目录(.)查找头文件 myhead.h

  • Makefile 中的 -c 参数
    表示只编译生成目标文件(.o),不进行链接。

  • 为什么要用头文件?
    头文件声明函数,实现代码分离,方便多人协作和模块化管理。

  • 为什么用 make 而不是手动编译?

    • 自动化编译流程,避免重复输入命令。

    • 只重新编译修改过的文件,提高效率。

7. 项目运行

(1)项目结构:

cal/
├── myhead.h
├── myadd.c
├── mydiv.c
├── result.c
└── Makefile

(2)编译运行:

make
./myresult

(3)输出结果:

Result: 8

(4)删除 .o 文件和 可执行文件

make clean   # 执行 Makefile 中的 clean 规则

相关文章:

  • 3.28日职001:大阪樱花vs浦和红钻,樱花攻守失衡,红钻有望全取三分
  • 解决Cubemx生产的 .ioc文件不能外部打开的方法
  • 格力智造的十年进击
  • 【AI学习】人工神经网络
  • Qt MSVC2017连接mysql数据库
  • 单纯形法详解
  • uniapp uni-swipe-action滑动内容排版改造
  • STM32F103_LL库+寄存器学习笔记09 - DMA串口接收与DMA串口发送,串口接收空闲中断
  • 软件需求未明确非功能性指标(如并发量)的后果
  • 聚势赋能:“人工智能+”激活高质量发展动能与生成式人工智能(GAI)认证的新机遇
  • IP 分片重组与 TCP 会话重组
  • EXPLAIN 计划中 filtered 含义及作用解析
  • stc8g1k08a软件SPI点亮屏幕MD144-QQVGA14P-01-V01(ILI9163C)测试
  • 阿里最新开源全模态大模型——Qwen2.5-Omni-7B,7B就能搞定“看听说写”,AI越来越像人了
  • 深度求索(DeepSeek):以AI之力重塑医疗未来
  • Linux—CentOS定时任务调度
  • milvus单节点安装教程
  • IP大洗牌ipv6强势来袭!!!【ipv6配置及应用】
  • 嵌入式c学习九
  • 机械臂如何稳稳上桌?Mujoco场景修改实操
  • 镇江网站建设网站/卖网站链接
  • quiz在哪个网站做/专业做网站公司
  • 佛山网站建设哪家效果好/百度指数有哪些功能
  • 建网站-湛江市/seo手机排名软件
  • 海外 国内网站建设/搜索引擎数据库
  • app应用网站html5模板/seo综合查询国产