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 <stdio.h>

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;
}
      123
     +123
123
+123
+123
-123
-123
000000123

 

示例2:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int len = 6;
    printf("%*d\n", len, 123);
    printf("%9.2f\n", 123.0);
    
    return 0;
}
   123
   123.00

 

示例3:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf("%hhd\n",12345);
    
    return 0;
}

在这里插入图片描述

12345=0x3039,0x39=57

改为(char)12345不产生警告

 

示例4:

#include <stdio.h>

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;
}
57
2
12345
5
12345ty
7

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

 

 

scanf%[flag]type

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

 

示例:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int num;
    scanf("%*d%d", &num);
    printf("%d\n", num); 
    
    return 0;
}

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

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

 

示例2:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int num;
    scanf("%i", &num);
    printf("%d\n", num); 
    
    return 0;
}

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

[^.]

示例:

在这里插入图片描述

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

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

 

 

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

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

示例:

#include <stdio.h>

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;
}

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

 

文本文件输入输出

><做重定向

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

示例:

#include <stdio.h>

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;
}

在这里插入图片描述

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 <stdio.h>

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;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 <stdio.h>

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;
}
 c=aa
~c=55
-c=56

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

按位取反和做补码计算结果是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 <stdio.h>

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;
}

编译时会产生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 <stdio.h>

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;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 <stdio.h>

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;
}

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;
};

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

 

示例2:

#include <stdio.h>

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");
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值