Skip to content

Latest commit

 

History

History
343 lines (333 loc) · 8.71 KB

l_07.md

File metadata and controls

343 lines (333 loc) · 8.71 KB

Lesson 7

C语言 文件读写 与 大程序结构


在这一课中,将会接触C语言的文件读写与多文件工程
文件读写为程序员提供了一种储存数据的方式
多文件工程实则为后期编程所必须的,它具有多种单文件工程无可比拟的优势

1. 文件读写

  • 数据流(stream): 程序与数据交互的方式 C语言可以读写文本文件(.txt)与二进制文件(.bytes)

    FILE *file = NULL;
    file = fopen("./rua.txt","w+");
    
    fclose(file);
  • fopen的参数? “r”:以只读的形式打开文本文件(不存在则出错) “w”:以只写的形式打开文本文件(不存在则新建,覆盖已有文件) “a”:以追加的形式打开文本文件(不存在则新建,指针固定在末尾)

    • 附加值: “+”:可读可写 “t”:txt文件(文本文件,默认可忽略) “b”:bytes文件(二进制文件,须写) ·
  • 输入与输出 C语言中将所有交互的设备(显示器、键盘等)都当作“文件” 输入输出 的概念是相对于程序而言的 例如: printf()的主要功能是向标准输出按规定格式输出信息 scanf()的主要功能是从标准输入按规定格式输入(读取)信息 在C语言的文件读写中,我们主要使用以下几个输入与输出函数 ·

  • 输出
    • 1. fputc()fputs()
      FILE *file = NULL;
      file = fopen("./rua.txt","w+");
      fputc('a',file);
      fputs("ywwuyi",file);
      //文件内容: aywwuyi
    • 2. fprintf
      FILE *file = NULL;
      file = fopen("./rua.txt","w+");
      fprintf(file,"%s","ywwuyi");    
      //文件内容: ywwuyi

·

  • 输入
    • 1. fgetc()fgets()

      FILE *file = NULL;
      file = fopen("./rua.txt","r+");
      char str[233];//可不可以用char *
      int a = fgetc(file);//注意返回值
      printf("%c\n",a);
      fgets(str,10,file);
      puts(str);
      /*输出: 
              1
              23456789y 
      */

      思考:为什么第二行不是 123456789y 或是 23456789yw ?

    • 扩展: 关于 fgets()

      fgets(str,10,stdin);

      stdin 指标准输入(缓存),通常意义上可以理解为你的键盘 因此,不难理解,上述语句的意思为给str输入一个字符串,长度保留为9 ·

    • 2. fscanf()

      FILE *file = NULL;
      file = fopen("./rua.txt","w+");
      char str[233];
      fprintf(file,"%s","Ywwuyi rua Mr.quin.");
      rewind(file);//这是什么?
      fscanf(file,"%s",str);
      puts(str);
      //输出:  Ywwuyi

·

  • 文件中定位
    • 1. rewind() 定义:将文件内部指针重新指向文件(流)开头
      FILE *file = NULL;
      file = fopen("./rua.txt","w+");
      char str[233];
      fprintf(file,"%s","Ywwuyi rua Mr.quin.");
      rewind(file);//①
      fscanf(file,"%s",str);
      puts(str);
      rewind(file);//②
      fprintf(file,"%s","233333");
      rewind(file);//③
      fscanf(file,"%s",str);
      puts(str);
    • 2. fseek() 定义:按照给定参数将文件内部指针指向文件(流)指定位置 int fseek(FILE *stream, long int offset, int whence) offset指偏移量,即相对于whence移动多少位置,负号前移,正号后移 whence指文件内的位置,通常为以下三个参数
      SEEK_SET 文件的开头
      SEEK_CUR 文件内指针当前位置
      SEEK_END 文件的末尾
      实例:
      FILE *file = NULL;
      file = fopen("./rua.txt","w+");
      char str[233];
      fprintf(file,"%s","Ywwuyi rua Mr.quin.");
      fseek(file,0,SEEK_SET);//等价于rewind(file)
      fscanf(file,"%s",str);
      puts(str);
      fseek(file,1,SEEK_CUR);
      fscanf(file,"%s",str);
      puts(str);
      fseek(file,-8,SEEK_END);
      fscanf(file,"%s",str);
      puts(str);
      /*输出:
            Ywwuyi
            rua
            Mr.quin.
      */

2. 大程序结构

大程序结构 含有多个源代码文件(C语言中为 .c) 每个源代码文件为一个编译单元 完成编译单元间的交互,需要链接器。 在诸如VS与DevC++的IDE(集成开发环境)中“创建项目”时便自带链接器 一般情况下,将多个.c文件放于同一个文件夹下面 在VScode中实现链接器的功能比较繁琐,因此此处用VS作工具说明 下面细嗦如何实现多文件的操作。 ·

  • 跨文件调用变量 main.c

    #include <stdio.h>
    extern int a;
    int main()
    {
        printf("%d", a);
        return 0;
    }

    rua.c

    #include <stdio.h>
    int a = 6;

    输出: 6

    • 注意点:extern关键字修饰变量的作用 ·
  • 跨文件调用函数 main.c

    void rua1();
    void rua2();
    int main()
    {
      rua1();
      rua2();
      return 0;
    }

    ywwuyi.c

    #include <stdio.h>
    void rua1()
    {
      puts("ywwuyi");
    }

    awsl.c

    #include <stdio.h>
    void rua1();
    void rua2()
    {
      puts("awsl");
      rua1();
    }

    输出:

    ywwuyi
    awsl
    ywwuyi
    • 思考:函数的原型,为什么不需要extern关键字? ·
  • 关于关键字

    • extern关键字 一般用作 声明 变量/函数 定义 全局变量/函数 时自带这个关键字 多文件程序中,extern使不同文件可以共用一个全局变量

    • static关键字 一般用作 定义 变量/函数 生命周期从整个程序开始到结束(全局与局部) 静态局部变量的作用域限制在代码块内 静态全局变量的作用域限制在编译单元内 多文件程序中,static使不同文件中可以定义同名事物 main.c

      #include <stdio.h>
      void rua();
      static int a = 233;
      int main()
      {
        rua();
        printf("%d\n", a);
        return 0;
      }

      ywwuyi.c

      #include <stdio.h>
      static int a = 1551;
      void rua()
      {
        printf("%d  ", a);
      }

      输出:1551 233 ·

  • #include操作

    将函数的原型放到头文件(.h文件)中, 便可以在需要调用这些函数的编译单元中#include该头文件, 来让编译器在编译的时候知道这些函数的原型。 · ywwuyi.cawsl.c 同上

    main.c

      #include "rua.h"
      int main()
      {
        rua1();
        rua2();
        return 0;
      }

    rua.h

    void rua1();
    void rua2();

    输出: 同上 ·

    • 注意点: 一般来说,.h文件用来放置声明。

    实际上,#include所做的事情是将该文件的所有文本原封不动地插入到该处位置 因此,#include并非是用来引入库文件的,stdio.h中仅含有函数的原型 stdio.h具体的函数定义在另外的地方,而现代C编译器则会默认引入所有的标准库 ·

  • 声明与定义

    思考:extern int aextern int a = 0 的区别? extern int a变量的声明 extern int a = 0变量的定义 声明 不另外分配任何内存空间 此处所述的函数原型也是一种声明。 只定义不声明的行为会遭到编译器指指点点× 只声明不定义的行为会遭到编译器重拳出击× ·

    • 重复声明 main.c
        #include "rua.h"
        void rua1();//函数rua1()被重复声明
        extern int a;//变量a被重复声明
        int main()
        {
          rua1();
          rua2();
          return 0;
        }
      rua.h
      void rua1();
      void rua2();
      extern int a;
      编译通过,说明可以重复声明 ·
    • 重复定义 main.c
      #include "rua.h"
      #include "yyds.h"
      rua.h
      int a;
      yyds.h
      #include "rua.h"
      编译不通过,说明不可重复定义 ·
    • 标准头文件结构 ——「重复定义の解决方法」 怎么解决重复定义的问题?
      • 鳖在.h文件里面定义! 写.h时鳖重复! (实际编程的时候其实经常碰到.h里面include另外一个.h的情况)
      • int a;改成extern int a,即改为 声明a变量 然后在某个文件里进行定义
      • 上面那些不好用,来搞点新东西! rua.h
        #ifndef _RUA_H_
        #define _RUA_H_
        int a;
        #endif
        或者 rua.h
        #pragma once
        int a;
        以上对.h文件的预处理使其成为 标准头文件
      • 注意点: 以上预处理仅在同一个编译单元中起作用 实际在不同文件中int a;(全局)仍然会出现错误 ·