• C语言程序设计笔记(浙大翁恺版) 第十三周:文件


    按照中国大学MOOC上浙江大学翁恺老师主讲的版本所作,B站上也有资源。原课程链接如下:

    https://www.icourse163.org/course/ZJU-9001

    由于是大三抽空回头整理的,所以可能前五章会记的内容比较简略。此外,作为选学内容的A0:ACLLib的基本图形函数和链表两章也没有做。西电的考试是机试,理论上学到结构体就能够应付考试了,但为了以后的学习考虑建议全学。

     

    其他各章节的链接如下:

    C语言程序设计笔记(浙大翁恺版) 第一周:程序设计与C语言

    C语言程序设计笔记(浙大翁恺版) 第二周:计算

    C语言程序设计笔记(浙大翁恺版) 第三周:判断

    C语言程序设计笔记(浙大翁恺版) 第四周:循环

    C语言程序设计笔记(浙大翁恺版) 第五周:循环控制

    C语言程序设计笔记(浙大翁恺版) 第六周:数据类型

    C语言程序设计笔记(浙大翁恺版) 第七章:函数

    C语言程序设计笔记(浙大翁恺版) 第八周:数组

    C语言程序设计笔记(浙大翁恺版) 第九周:指针

    C语言程序设计笔记(浙大翁恺版) 第十周:字符串

    C语言程序设计笔记(浙大翁恺版) 第十一周:结构类型

    C语言程序设计笔记(浙大翁恺版) 第十二周:程序结构

    C语言程序设计笔记(浙大翁恺版) 第十三周:文件

     

    文件

    文件

    格式化输入输出

    格式化的输入输出

    printf %[flags][width][.prec][hIL]type

    scanf %[flag]type

     

     

    %[flags][width][.prec][hIL]type

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
     

    示例:

    #include 
    
    int main(int argc, char const *argv[])
    {
        printf("%9d\n", 123);
        printf("%+9d\n", 123);
        printf("%-9d\n", 123);
        printf("%+-9d\n", 123);
        printf("%-+9d\n", 123);
        printf("%-+9d\n", -123);
        printf("%-9d\n", -123);
        printf("%09d\n", 123);
        // printf("%-09d\n", -123);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
          123
         +123
    123
    +123
    +123
    -123
    -123
    000000123
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

     

    示例2:

    #include 
    
    int main(int argc, char const *argv[])
    {
        int len = 6;
        printf("%*d\n", len, 123);
        printf("%9.2f\n", 123.0);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
       123
       123.00
    
    • 1
    • 2

     

    示例3:

    #include 
    
    int main(int argc, char const *argv[])
    {
        printf("%hhd\n",12345);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    12345=0x3039,0x39=57

    改为(char)12345不产生警告

     

    示例4:

    #include 
    
    int main(int argc, char const *argv[])
    {
        int num;
        printf("%hhd%n\n", (char)12345, &num);
        printf("%d\n", num);
        
        printf("%d%n\n", 12345, &num);
        printf("%d\n",num);
        
        printf("%dty%n\n",12345, &num);
        printf("%d\n",num);    
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    57
    2
    12345
    5
    12345ty
    7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    %n表示printf到此时已经输出了多少字符,然后填到指针所指的变量里

     

     

    scanf%[flag]type

    在这里插入图片描述
    在这里插入图片描述

     

    示例:

    #include 
    
    int main(int argc, char const *argv[])
    {
        int num;
        scanf("%*d%d", &num);
        printf("%d\n", num); 
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iy53sgdc-1659892983885)(C语言程序设计.assets/image-20220803165042678.png)]

    -o选项指定编译产生的可执行文件名

     

    示例2:

    #include 
    
    int main(int argc, char const *argv[])
    {
        int num;
        scanf("%i", &num);
        printf("%d\n", num); 
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cwjku7VR-1659892983887)(C语言程序设计.assets/image-20220803165454196.png)]
     

    [^.]

    示例:

    在这里插入图片描述

    这是GPS模块产生的数据,以”$“开头,所有字段以”,“分隔,有时两个”,“之间可能没有内容,用scanf读入

    %*[^.]表示省略到逗号之前的所有内容,%[^.]表示到逗号之前的所有内容作为字符串读入

     

     

    printfscanf的返回值分别是读入的项目数和输出的字符数

    在要求严格的程序中,应该判断每次调用scanfprintf的返回值,从而了解程序运行中是否存在问题

    示例:

    #include 
    
    int main(int argc, char const *argv[])
    {
        int num;
        int i1 = scanf("%i", &num);
        int i2 = printf("%d\n", num);
        printf("%d:%d\n", i1, i2);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Yh8Wa5M-1659892983889)(C语言程序设计.assets/image-20220803191517431.png)]

     

    文本文件输入输出

    ><做重定向

    如果程序是用printfscanf来做输入输出的,可以用程序运行时的重定向,<指定一个文件作为输入,>指定一个文件写入输出

    示例:

    #include 
    
    int main(int argc, char const *argv[])
    {
        int num;
        int i1 = scanf("%i", &num);
        int i2 = printf("%d\n", num);
        printf("%d:%d\n", i1, i2);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    cat打开一个文件,重定向到一个文件从而可以往里写内容

     

     

    FILE

    stdio.h头文件里已经声明了FILE*类型

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMDPvLlf-1659892983893)(C语言程序设计.assets/image-20220803192741048.png)]

     

     

    打开文件的标准代码

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h8XSOmH0-1659892983895)(C语言程序设计.assets/image-20220803192818031.png)]
    指针变量fp指向一个FILE结构,该结构代表了打开文件的一些信息

    fopen没有打开文件会返回NULL

     

    示例:

    #include 
    
    int main(int argc, char const *argv[])
    {
        FILE *fp = fopen("12.in", "r");
        if ( fp ) {
            int num;
            fscanf(fp, "%d", &num);
            printf("%d\n", num);
            fclose(fp);
        } else {
            printf("无法打开文件\n");
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GsEyz7eP-1659892983896)(C语言程序设计.assets/image-20220803193110607.png)]

     

     

    fopen

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cbfNsfAL-1659892983897)(C语言程序设计.assets/image-20220803193148189.png)]

     

    二进制文件

    之前见的所有文件都是文本文件,输入输出都是文本方式。当printf一个int时实际上是把这个int格式化成人可读的字符串形式,该int在计算机内部是二进制形式,用4B表达。当输入一个int时,scanf扫描输入字符串从中识别出int填到一个int类型变量里

     

    二进制文件

    其实所有的文件最终都是二进制的,只不过文本文件表现为人可以阅读的形式,无非就是可以用最简单的方式读写

    在Unix下,有很多简单的工具对文本文件进行操作,如可用moretailcatvi打开和编辑文件。但是二进制文件,如可执行文件、JPG图片、MP3音频等需要专门的程序来查看和读写

     

     

    文本 vs 二进制

    Unix喜欢用文本文件来做数据存储和程序配置

    • 交互式终端的出现使得人们可以喜欢用文本和计算机“talk”,输入和输出可见都是文本
    • Unix的shell提供了一些可以读写的程序

    Windows喜欢用二进制文件

    • DOS是草根文化,并不继承和熟悉Unix文件
    • PC刚开始的时候能力有限,DOS的能力更有先,二进制更接近底层

     

    文本的优势是方便人类读写,而且跨平台。文本的缺点是程序输入输出需要格式化,开销大

    二进制的的缺点是人类读写困难,而且不跨平台(int的大小不一致,大小端的问题 …)。二进制的优点是程序读写快

     

     

    程序为什么要操作文件

    配置:程序有配置信息,比如窗口大小、字体颜色等等。Unix传统用文本文件配置数据,直接用vi就能编辑。Windows传统将这些配置数据写在注册表里,注册表是一个非常大的二进制文件,整个Windows所有软件的配置信息都写在里面,需要用专门的工具如regedit注册表编辑器才能编辑

    数据: 稍微有点量的数据都放数据库了。有数据库的接口库,用函数调用读写数据库

    媒体:图片、声音、视频等肯定是二进制的

    现实是,程序通过第三方库来读写文件,很少直接读写二进制文件了

     

     

    二进制读写

    在这里插入图片描述

    ptr指向要读写的内存,size表示这块内存有多大,nitems表示有几个这样的内存,stream是文件指针

    二进制文件的读写一般都是通过对一个结构变量的操作来进行的,要写的数据在一个结构里,一次写一个或多个结构。所以size其实指的是一个结构的大小,nitems就是用来说明这次读写几个结构变量

     

    示例:
    在这里插入图片描述
     

     

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
     

     

    在这里插入图片描述

    在这里插入图片描述
    用图形工具打开二进制文件student.data,左边是二进制数据,右边试图把这些二进制数据变成字符串让用户看到

     

     

    在文件中定位

    在这里插入图片描述
     

    示例:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

     

     

    可移植性

    这样的二进制不具有可移植性。如在int为32位的机器上写成的数据文件无法直接在int为64位的机器上正确读出

    解决方案之一是放弃使用int,而是typedef具有明确大小的类型

    更好的方案是用文本

     

    位运算

    按位运算

    按位运算

    C有这些按位运算的运算符

    • & 按位的与
    • | 按位的或
    • ~ 按位取反
    • ^ 按位的异或
    • << 左移
    • >> 右移

     

    按位与&

    如果 ( x ) i = = 1 (x)_i ==1 (x)i==1并且 ( y ) i = = 1 (y)_i==1 (y)i==1,那么 ( x & y ) i = 1 (x\&y)_i=1 (x&y)i=1,否则 ( x & y ) i = 0 (x\&y)_i=0 (x&y)i=0

    按位与常用于两种应用:

    • 让某一位或某些位为0:x & 0xFE
    • 去某一个数中的某一段:x & 0xFF

     

     

    按位或|

    如果 ( x ) i = = 1 (x)_i ==1 (x)i==1 ( y ) i = = 1 (y)_i==1 (y)i==1,那么 ( x ∣ y ) i = 1 (x|y)_i=1 (xy)i=1,否则 ( x ∣ y ) i = 0 (x|y)_i=0 (xy)i=0

    按位或常用于两种应用:

    • 让某一位或某些位为1:x | 0x01
    • 把两个数拼起来:0x00FF & 0xFF00

     

     

    按位取反~

    ( ∼ x ) i = 1 − ( x ) i (\sim x)_i=1-(x)_i (x)i=1(x)i,把1位变0,0位变1

    • 想得到全部位为1的数:~0
    • 7的二进制是0111,x | 7使得低3位为1,而x & ~7就使得低3位为0

     

    按位取反和算补码不一样,算补码要做减法

    在这里插入图片描述
     

    示例:

    0xAA的按位取反和做补码

    #include 
    
    int main(int argc, char const *argv[])
    {
        unsigned char c = 0xAA;
        printf(" c=%hhx\n", c);
        printf("~c=%hhx\n", (char)~c);
        printf("-c=%hhx\n", (char)-c);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
     c=aa
    ~c=55
    -c=56
    
    • 1
    • 2
    • 3

    有符号数求相反数的实现就是求补码

    按位取反和做补码计算结果是int,期望作为一个字节大小的整数输出,所以类型转换为char

     

     

    逻辑运算 vs 按位运算

    对于逻辑运算,它只看到两个值:0和1

    可以认为逻辑运算相当于把所有非0值都变成1,然后做按位运算

    示例:

    5 & 4 —> 4 而 5 && 4 —> 1 & 1 —> 1

    5 | 4 —> 5 而 5 || 4 —> 1 | 1 —> 1

    ~4 —> 3 而 !4 —> !1 —> 0

     

     

    按位异或^

    如果 ( x ) i = = ( y ) i (x)_i ==(y)_i (x)i==(y)i,那么 ( x ^ y ) i = 0 (x \verb|^| y)_i=0 (x^y)i=0,否则 ( x ^ y ) i = 1 (x \verb|^| y)_i=1 (x^y)i=1

    • 如果xy相等,那么x ^ y的结果为0
    • 对一个变量用同一个值异或两次,等于什么也没做:x ^ y ^ y 等于x

     

    移位运算

    左移<<

    i << j i中所有的位向左移动j个位置,而右边填入0

    所有小于int的类型,移位以int的方式来做,结果是int

    x << 1等价于 x = x × 2 x=x\times2 x=x×2x << n等价于 x = x × 2 n x=x\times 2^n x=x×2n,最多可以移多少位取决于int有多大

     

    示例:

    #include 
    
    int main(int argc, char const *argv[])
    {
        unsigned char c = 0xA5;
        printf("   c=%hhx\n", c);
        printf("c<<2=%hhx\n", c<<2);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    编译时会产生warning,当前编译器所有的按位运算结果都被当作是int

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j9iKhB54-1659892983920)(C语言程序设计.assets/image-20220804161144291.png)]
     

    如果将printf("c<<2=%hhx\n", c<<2)改为printf("c<<2=%x\n", c<<2)按照int的方式输出,得

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uk6F5xZx-1659892983921)(C语言程序设计.assets/image-20220804161509678.png)]
     

    如果改为printf(" c=%d\n", c)printf("c<<2=%d\n", c<<2)当作十进制输出,得

    在这里插入图片描述

    165和660之间是4倍关系

     

     

     

    右移>>

    i >> j i中所有的位向右移j

    所有小于int的类型,移位以int的方式来做,结果是int

    对于unsigned的类型,左边填入0。对于signed的类型,左边填入原来的最高位(保持符号不变)

    x >> 1等价于 x = x / 2 x=x/2 x=x/2x << n等价于 x = x / 2 n x=x /2^n x=x/2n

     

    示例:

    #include 
    
    int main(int argc, char const *argv[])
    {
        int a = 0x80000000;
        // 1000000000000...000
        // 11000000...00000000
        
        unsigned int b = 0x80000000;
        printf("a=%d\n", a);
        printf("b=%u\n", b);
        printf("a>>1=%d\n", a>>1);
        printf("b>>1=%u\n", b>>1);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-he7AvTqu-1659892983923)(C语言程序设计.assets/image-20220804162952243.png)]
     

    如果将printf("a>>1=%d\n", a>>1)printf("b>>1=%u\n", b>>1)改为printf("a<<1=%u\n", a<<1)printf("b<<1=%u\n", b<<1)做左移

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HDjUOVAK-1659892983924)(C语言程序设计.assets/image-20220804163437029.png)]
     

     

    移位的位数不要用负数,这是没用定义的行为

     

    位运算例子

    输出一个数的二进制

    示例:

    #include 
    
    int main(int argc, char const *argv[])
    {
        int number;
        scanf("%d", &number);
        unsigned mask = 1u<<31;
        for ( ; mask ; mask >>=1 ) {
            printf("%d", number & mask?1:0);
        }
        printf("\n");
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    unsigned mask会被编译器理解为unsigned int mask

    1u表示最低位为1的unsigned

     

    按照器件手册在单片机程序中用位运算设置特殊功能寄存器(SFR)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nGN8Zm89-1659892983924)(C语言程序设计.assets/image-20220804164624457.png)]
     

    位段

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcZDfb1R-1659892983925)(C语言程序设计.assets/image-20220804170602373.png)]
     

    位段

    为了简单地控制多个bit,把一个int的若干位组合成一个结构

    示例:

    struct {
        unsigned int leading : 3;
        unsigned int FLAG1 : 1;
        unsigned int FLAG2 : 1;
        int trailing : 11;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    unsigned int leading : 3;表示这个成员占3个bit,最后可能所有成员放在一个int里,而每一个成员占据其中的某些bit

     

    示例2:

    #include 
    
    void prtBin(unsigned int number);
    
    struct U0 {
        unsigned int leading : 3;
        unsigned int FLAG1 : 1;
        unsigned int FLAG2 : 1;
        int trailing : 27;
    };
    
    int main(int argc, char const *argv[])
    {
        struct U0 uu;
        uu.leading = 2;
        uu.FLAG1 = 0;
        uu.FLAG2 = 1;
        uu.trailing = 0;
        printf("sizeof(uu)=%lu\n", sizeof(uu));
        prtBin(*(int*)&uu);
        
        return 0;
    }
    
    void prtBin(unsigned int number)
    {
        unsigned mask = 1u<<31;
        for ( ; mask ; mask >>=1 ) {
            printf("%d", number & mask?1:0);
        }
        printf("\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CX3AuUyc-1659892983927)(C语言程序设计.assets/image-20220804171714574.png)]
     

    如果注释掉int trailing : 27;uu.trailing = 0,得

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zxxKbe2P-1659892983927)(C语言程序设计.assets/image-20220804171902184.png)]

    前面的部分没有赋初值

     

    如果将int trailing : 27;改为int trailing : 32;,得

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ybii7183-1659892983928)(C语言程序设计.assets/image-20220804172030296.png)]

    此时所有的位数加起来已经超过了32,所以需要两个int来表达这个位段

     

     

    可以直接用位段的成员名称来访问,比移位、与、或还方便

    编译器会安排其中的位的排列,不具有可移植性,在演示的编译器上从最右边排起,可能别的就是从最左边排起

    当最所需的位超过一个int时会采用多个int

  • 相关阅读:
    「Redis数据结构」RedisObject
    CNVD-2021-27648:锐捷RG-UAC统一上网行为管理与审计系统信息泄露漏洞复现
    【c++】向远程服务器传输文件实现
    Notion+Zotero+Notero 联动教程(23年9月更新版)
    JVM 内存管理 你知道多少
    SQL按月生成分区表,按月份查询该表数据
    类欧几里得算法
    string的模拟实现
    网络原理之封装和分用,网络编程套接字
    「分享学习」SpringCloudAlibaba高并发仿斗鱼直播平台实战完结
  • 原文地址:https://blog.csdn.net/zimuzi2019/article/details/126220163