Linux-I/O

本文详细介绍了标准I/O的基本概念、流的概念及不同类型的缓存,并提供了多个示例代码来展示如何使用标准I/O函数进行文件操作。同时,文章还探讨了文件I/O的特性及其常用函数,包括打开文件、读写数据、定位等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标准I/O

一、标准I/O概述

 1.文件指针

       FILE指针:每个被使用的文件都在内存中开辟一个区域,用来存放文件的有关信息,这些信息保存在一个结构体类型的变量中,该结构体类型是由系统定义的,取名为FILE。

 2.标准I/O库的所有操作都是围绕流(stream)来进行的,在标准I/O中流用FILE*来描述;

 3.流(stream):所有I/O操作仅是简单的从程序移进或者移出,这种字节流就称为流;

4.标准I/O提供三种类型的缓存:

  • 全缓冲:当填满I/O缓存后才进行实际I/O操作;
  • 行缓存:当输入和输出中遇到符号('\n')时,才进行I/O操作;
  • 不带缓存:标准I/O库不对字符进行缓存,例如:stderr

5.使用setbuf()和setvbuf()可以更改缓存的类型;

6.刷新(fflush):标准I/O的写操作;在任何时刻都可以使用fflush强制刷新一个数据流(此函数使该流所有未写的数据都被传递至内核);

#include<stdio.h>
int main (void)
{
     static char buf[1024];
	//setbuf(stdout, buf);
	printf("aaaaaaaaaa");
	fflush(stdout); //没有将不会打印aaaaaaa
	while (1);
    return 0;
}
  • 标准输入           0   stdin
  • 标准输出          1    stdout
  • 标准错误输出   2    stderr

二、标准I/O的函数

(一)打开一个标准I/O流

       #include <stdio.h>

       FILE *fopen(const char *path, const char *mode);
       path --- 打开文件的路径

       mode --- 打开文件的方式:r,r+,w,w+,a,a+

#include<stdio.h>
int main()
{
    FILE *fp;
    fp = fopen("test.c","r+");
    if(fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    printf("success\n");
    if(EOF == fclose(fp))
    {
        perror("fclose");
        return -1;
    }
}

(二)一次读写一个字符

       #include <stdio.h>

       int fgetc(FILE *stream);
       int fputc(int c, FILE *stream);

 

       返回值:成功则为下一次字符

                      出错 --- EOF

int main()
{
    int ch;
    while(EOF != (ch = fgetc(stdin)))
    {
        fputc(ch,stdout);
    }
    return 0;
}

(三)一次输入/输出一行

       #include <stdio.h>

       char *fgets(char *s, int size, FILE *stream);  //stream->s
       int fputs(const char *s, FILE *stream);   //s -> stream
       返回值:成功则为buf

                      EOF --- 出错


int main()
{
    char buf[1024];
    while(1)
    {
        if(NULL != fgets(buf,sizeof(buf),stdin))
        fputs(buf,stdout);
    }
    return 0;
}
 

 示例代码:

#include<stdio.h>
#include<string.h>
int main(int argc,char *argv[])
{
    if(argc<3)
    {
        fprintf(stderr,"Usage: %s <file1name> <file2name>\n",argv[0]);
        return -1;
    }
    FILE *fp = fopen(argv[1],"r+");
    if( fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    FILE *fp1 = fopen(argv[2],"w+");
    if( fp1 == NULL)
    {
        perror("fopen");
        return -1;
    }
    char buf[100];
    while(fgets(buf,sizeof(buf),fp) != NULL)
    {
            fputs(buf,fp1);
    }

    return 0;
}

(四)整体读写

       #include <stdio.h>

       size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); //stream->ptr,大小->nmemb

       size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); //ptr->stream

#include<stdio.h>
#include<string.h>
int main(int argc,char *argv[])
{
    if(argc<3)
    {
        fprintf(stderr,"Usage: %s <file1name> <file2name>\n",argv[0]);
        return -1;
    }
    FILE *fp = fopen(argv[1],"r+");
    if( fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    FILE *fp1 = fopen(argv[2],"w+");
    if( fp1 == NULL)
    {
        perror("fopen");
        return -1;
    }
    char buf[100];
    int ret;
    while(1)
    {
        ret = fread(buf,1,sizeof(buf),fp);
        if(ret<0)
        {
            perror("fread");
            break;
        }
        else if(ret == 0)
        {
            printf("read file end\n");
            break;
        }
        fwrite(buf,1,ret,fp1);
    }
    fclose(fp);
    fclose(fp1);
    return 0;
}

(五)定位流fseek()

       #include <stdio.h>

       int fseek(FILE *stream, long offset, int whence);
       offset ---> 偏移的字节数

       whence --->  SEEK_SET --- 文件头

                             SEEK_CUR --- 当前位置

                             SEEK_END --- 文件尾

       返回值:0 --- 成功

                     -1 --- 失败

if(0>fseek(fp,,100,SEEK_SET))
{
    perror("fseek");
    return -1;
}

文件I/O

一、文件I/O的性质

  1. 不带缓冲;
  2. 通过文件描述符来访问文件(fd),是一个非负整数;

二、文件I/O的函数

(一)打开文件

       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

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

       flags --- 打开方式

       mode --- 权限

O_RDONLY只读
O_WRONLY可写
O_RDWR读写
O_CREAT不存在新建,并设置权限
O_TRUNC存在删除原有数据
O_APPEND追加

      返回值:成功返回文件描述符

                     失败返回-1,并设置errno

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc,char *argv[])
{
    int fd;
    fd = open("test.c",O_WRONLY|O_CREAT|O_EXCCL,0644);
    if(fd<0)
    {
        perror("open");
        return -1;
    }
    printf("open success!\n");
    close(fd);
    return 0;
}

(二)读写数据

       #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);  //fd -> buf
       ssize_t write(int fd, const void *buf, size_t count); //buf -> fd
       返回值:成功 --- 字节数

                     0 --- 表示达到文件末尾

                     -1 --- 出错,并设置erron

示例代码1:


#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc,char *argv[])
{
    if(argc < 3)
    {
        fprintf(stdout,"Usage: %s <file1name> <file2name>",argv[0]);
        return -1;
    }
    int fd1;
    int fd2;
    fd1 = open(argv[1],O_RDONLY);
    fd2 = open(argv[2],O_WRONLY);
    if( fd1 < 0)
    {
        perror("open");
        return -1;
    }
    char buf[100];
    int ret;
 /*   while(1)
    {
        ret = read(fd1,buf,sizeof(buf));
        if(ret < 0)
        {
            perror("read");
            break;
        }
        else if(ret = 0)
        {
            perror("read");
            break;
        }
        write(fd2,buf,ret);
    }
    */
    while(read(fd1,buf,sizeof(buf)) > 0)
    {
        write(fd2,buf,sizeof(buf));
    }
    close(fd1);
    close(fd2);
    return 0;
}

示例代码2:

/*************************************************************************
	> File Name: open.c
	> Author: 
	> Mail: 
	> Created Time: 2017年08月08日 星期二 11时41分42秒
 ************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc,char *argv[])
{
    if(argc<2)
    {
        fprintf(stdout,"Usage: %s <filename>",argv[0]);
        return -1;
    }
    int fd;
    fd = open(argv[1],O_WRONLY);
    if(fd<0)
    {
        perror("fd");
        return -1;
    }
    char buf[100];
    int ret;
    while(1)
    {
       ret = read(fd,buf,sizeof(buf)); 

            if(ret<0)
            {
                break;
            }
        write(fd,buf,ret);
    }
    close(fd);
    return 0;
}

(三) 定位

       #include <sys/types.h>
       #include <unistd.h>

       off_t lseek(int fd, off_t offset, int whence);
       offset --- 偏移字节数

       whence ---> SEEK_SET --- 头

                            SEEK_CUR --- 当前位置

                            SEEK_END --- 文件末尾

获取文件/目录的属性信息

       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>

       int stat(const char *path, struct stat *buf);
       int fstat(int fd, struct stat *buf);
       int lstat(const char *path, struct stat *buf);

       返回值 :0 --- 成功

                     -1 --- 出错,并设置errno

目录操作相关函数

chdir()/opendir()/readdir()

#include <stdio.h>
#include <dirent.h>

int main()
{
	DIR *dirp = opendir(".");
	struct dirent *file;

	while (file = readdir(dirp)) {
		printf("filename: %s\n", file->d_name);
	}
	closedir(dirp);

	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<think>嗯,用现在提供了多个crt1.o文件的路径,并希望我分析为什么会报缺失的错误。首先,我需要回顾之前的对话,用之前遇到的是在编译时缺少crt1.o等文件的问题,可能是在交叉编译Android应用时出现的。现在用给出了系列不同路径下的crt1.o文件,看起来是不同工具链或系统库中的文件。 首先,我需要理解每个路径对应的架构和工具链。例如,路径中包含arm-linux-gnueabihf、aarch64-linux-gnu、x86_64等,这些都是不同的目标架构。用之前使用的是Android NDK的arm64架构,现在这些路径中有arm和aarch64的crt1.o,可能用是在不同的环境中尝试编译,或者有多个工具链安装。 接下来,用的问题可能是链接器找不到正确的crt1.o文件。为什么会这样?可能的原因包括工具链配置错误、编译参数中未正确指定sysroot或gcc-toolchain路径,或者链接器搜索路径中没有包含正确的目录。 比如,用提供的路径中有两个来自rv1126_rv1109_linux/prebuilts/gcc的路径,这可能是Rockchip平台提供的交叉编译工具链,而其他路径如/usr下的可能是系统安装的工具链。如果用在编译时没有正确指定使用哪个工具链,链接器可能会在默认路径中查找,而找不到对应的crt1.o。 另个可能性是架构不匹配。比如,用的目标平台是arm64(aarch64),但链接器可能错误地使用了armhf(arm-linux-gnueabihf)的crt1.o,导致不兼容。或者,编译命令中的--target参数不正确,导致选择了错误的架构。 此外,用之前的错误可能涉及到NDK r18b的路径配置问题,现在用可能尝试使用不同的工具链,但同样遇到了路径配置的问题。需要检查编译命令中是否正确指定了sysroot、-gcc-toolchain以及库路径。 还需要注意,不同工具链的crt1.o文件可能位于不同的子目录下,例如在gcc-linaro-6.3.1和gcc-arm-8.3的路径下都有crt1.o,但版本不同,可能存在兼容性问题。如果用指定了错误的工具链版本,也可能导致链接失败。 总结可能的错误原因: 1. 工具链路径未正确配置,导致链接器找不到对应的crt1.o。 2. 目标架构与工具链不匹配,例如为aarch64编译但使用了armhf的工具链。 3. 编译命令中缺少必要的参数,如--sysroot、-gcc-toolchain等。 4. 多个工具链存在,导致链接器混淆,错误地选择了不匹配的文件。 解决方法可能包括: - 确认目标架构,选择对应的工具链路径。 - 在编译命令中明确指定sysroot和gcc-toolchain路径。 - 检查链接器的搜索路径是否包含正确的crt1.o目录。 - 确保所有相关库和启动文件都来自同工具链,避免混合使用不同来源的文件。 现在需要将这些分析整理成结构化的回答,帮助用逐步排查问题。同时,根据用提供的路径,指出哪些路径可能相关,以及如何正确配置编译参数。</think>--- ### 、路径解析与架构对应关系 根据您列出的多个`crt1.o`路径,可以明确观察到不同架构和工具链的混合存在。以下是关键分类: | 路径 | 目标架构 | 工具链类型 | 适用场景 | |------|----------|------------|----------| | `/home/chang/rv1126_rv1109_linux/prebuilts/gcc/.../arm-linux-gnueabihf/libc/usr/lib/crt1.o` | **ARMv7 (32位)** | Linaro 6.3.1 | Rockchip RV1126/RV1109开发板(Cortex-A7/A9) | | `/home/chang/rv1126_rv1109_linux/prebuilts/gcc/.../gcc-arm-8.3/arm-linux-gnueabihf/libc/usr/lib/crt1.o` | **ARMv7 (32位)** | ARM官方工具链 8.3 | 兼容性更好的ARMv7编译 | | `/usr/aarch64-linux-gnu/lib/crt1.o` | **ARM64 (AArch64)** | 系统默认交叉工具链 | 通用ARM64设备(如RK3588) | | `/usr/arm-linux-gnueabihf/lib/crt1.o` | **ARMv7硬浮点** | 系统默认交叉工具链 | 通用ARMv7设备 | --- ### 二、报错"缺失crt1.o"的典型原因 #### 1. **架构不匹配**(最常见) **场景示例**: ```bash # 目标架构声明为ARM64 --target=aarch64-linux-android21 # 但链接器实际搜索ARMv7路径(错误) -L/usr/arm-linux-gnueabihf/lib ``` **诊断方法**: ```bash # 查看编译器默认搜索路径 echo | aarch64-linux-gnu-gcc -v -x c - 2>&1 | grep COLLECT_LTO_WRAPPER # 输出应包含类似:/usr/lib/gcc/aarch64-linux-gnu/11/... ``` #### 2. **工具链路径未正确指定** **错误现象**: ```bash # 未指定sysroot导致搜索系统默认路径 gcc -o app main.c # 可能错误链接到x86_64的crt1.o(灾难性后果) ``` **修复方案**: ```bash # 显式指定sysroot和工具链 export SYSROOT=/home/chang/rv1126_rv1109_linux/prebuilts/gcc/.../sysroot arm-linux-gnueabihf-gcc --sysroot=$SYSROOT -o app main.c ``` #### 3. **多工具链污染环境** **典型冲突**: - 系统安装的交叉工具链(`/usr/...`) - Rockchip预编译工具链(`rv1126_rv1109_linux/prebuilts`) - NDK工具链(`android-ndk`) **解决方案**: ```bash # 通过绝对路径调用编译器 /home/chang/rv1126_rv1109_linux/prebuilts/gcc/.../arm-linux-gnueabihf-gcc main.c ``` --- ### 三、系统性排查步骤 #### 1. 确认目标架构 ```bash # 查看开发板CPU信息(示例为RV1126) $ cat /proc/cpuinfo Processor : ARMv7 Processor rev 5 (v7l) # ARMv7架构 ``` #### 2. 验证工具链与架构对应 ```bash # 检查编译器默认目标 arm-linux-gnueabihf-gcc -dumpmachine # 期望输出:arm-linux-gnueabihf ``` #### 3. 强制指定链接路径 在编译命令中显式添加: ```bash -Wl,--dynamic-linker=/home/chang/rv1126_rv1109_linux/prebuilts/.../lib/ld-linux-armhf.so.3 \ -Wl,-rpath=/home/chang/rv1126_rv1109_linux/prebuilts/.../lib ``` #### 4. 使用`readelf`验证ELF格式 ```bash # 检查已编译程序架构 readelf -h app | grep Machine # 正确输出:ARM (0x28) 或 AArch64 (0xB7) ``` --- ### 四、编译参数最佳实践(以RV1126为例) ```bash export TOOLCHAIN=/home/chang/rv1126_rv1109_linux/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf # 编译命令 $TOOLCHAIN/bin/arm-linux-gnueabihf-gcc \ --sysroot=$TOOLCHAIN/arm-linux-gnueabihf/libc \ -I$TOOLCHAIN/arm-linux-gnueabihf/include \ -L$TOOLCHAIN/arm-linux-gnueabihf/lib \ -Wl,-rpath-link=$TOOLCHAIN/arm-linux-gnueabihf/lib \ -o my_app main.c ``` --- ### 五、高级调试技巧 #### 1. 跟踪链接器搜索路径 ```bash # 显示详细链接过程 arm-linux-gnueabihf-gcc -Wl,--verbose main.c 2>&1 | grep succeeded # 输出应包含正确路径:/home/chang/rv1126_.../crt1.o ``` #### 2. 手动验证库依赖 ```bash # 检查动态库依赖项 arm-linux-gnueabihf-readelf -d my_app | grep NEEDED # 确认所有依赖库路径有效 ``` #### 3. 交叉编译QEMU验证 ```bash # 在x86主机上运行ARM程序 sudo apt install qemu-user-static qemu-arm-static ./my_app ``` --- ### 六、架构差异对比表 | 特征 | ARMv7 (RV1126) | ARM64 (RK3588) | |---------------------|------------------------|------------------------| | 工具链前缀 | arm-linux-gnueabihf | aarch64-linux-gnu | | 默认浮点运算 | 硬件浮点 (hf) | 自带浮点指令集 | | 典型`crt1.o`路径 | `.../libc/usr/lib/` | `.../aarch64-linux-gnu/lib/` | | 动态链接器 | ld-linux-armhf.so.3 | ld-linux-aarch64.so.1 | 通过以上分析,您应该能准确定位到架构或路径配置错误的具体环节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值