一、初识C语言
1.C语言仅有32个关键字,9种控制语句,34种运算符,却能完成无数的功能:
2.C语言分布编译:
1)预处理 宏定义展开 头文件展开 条件编译 去掉注释
2)编译 检查语法 将C语言转成汇编语言
3)汇编 将汇编语言转成机器语言(二进制码)
4)链接 将C语言依赖库链接到程序中
3.printf输出格式
二、数组
1.一维数组定义和使用
1)数组名是一个地址常量,指向数组首地址的常量 arr是地址
2)方括号[]中常量表达式表示数组元素的个数 其下标索引是从0开始
2.一维度数组的初始化
1)int a[10] = { 0 };//所有的成员都设置为0 只要后面没有定义到的都初始化为0
2) int a[] = { 1, -2, 3,- 4, 5, -6, 7, -8, -9, 10 }; //用花括号
3.数组名
1)数组名是一个地址的常量,代表数组中首元素的地址。
printf("a = %p\n", a);
printf("&a[0] = %p\n", &a[0]); //是一样的地址
2)所以可以用于求取数组长度
for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("%d ", a[i]);
}
4.二维数组
1.定义
1)按照行进行存放;先存放a[0]行,再存放a[1]行、a[2]行
2.初始化
1)分段赋值
int a[3][4] =
{
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8, },
{ 9, 10, 11, 12 }
};
2)连续赋值
int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };
3)//可以只给部分元素赋初值,未初始化则为0
int a[3][4] = { 1, 2, 3, 4 };
4)//所有的成员都设置为0
int a[3][4] = {0};
3.数组名
数组名是一个地址的常量,代表数组中首元素的地址。
//值是一样的 首地址
Printf('%p\n’,arr)
Printf('%p\n’,arr[0])
Printf('%p\n’,&arr[0][0])
//数组输出用for等循环遍历
三、字符串
1.字符串与字符数组
1)C语言中没有字符串这种数据类型,可以通过char的数组来替代;
2)字符串一定是一个char的数组,但char的数组未必是字符串;(字符串是char数组的特例)
3)数字0(和字符‘\0’等价)结尾的char数组就是一个字符串,但如果char数组没有以数字0结尾,那么就不是一个字符串,只是普通字符数组,所以字符串是一种特殊的char的数组。
Ps. Char arr[6]={ 'h', 'l', 'l', 'e', 'o' } 后面自动补一个0 数字0
Char arr[6]={ 'h', 'l', 'l', 'e', 'o','\0' }
Char *arr=”hello”
是一样的,都是字符串
char ch[10]; 再输入时候最多输入9个字符,否则就不是字符串了 字符数组 没有\0结尾
2.字符串初始化
1)以0或者、\0为结束标志
可以自动补0 经常使用
3.字符串的输入和输出
由于字符串采用了'\0'标志,字符串的输入输出将变得简单方便。
1)gets:比如键盘敲入aaa aaa 输出也为aaa aaa
2)scanf:比如键盘敲入aaa aaa 输出也为aaa
3)fgets:fgets()在读取一个用户通过键盘输入的字符串的时候,同时把用户输入的回车也做为字符串的一部分。通过fgets结尾多了“\n”。fgets()函数是安全的,不存在缓冲区溢出的问题。(不会溢出)
4)puts:在输出完成后自动输出一个'\n'。
5)fputs:将str所指定的字符串写入到stream指定的文件中, 字符串结束符 '\0' 不写入文件。
stream:文件指针,如果把字符串输出到屏幕,固定写为stdout
四、函数
1.生成随机数
2.形式参数:在未出现函数调用时,它们并不占内存中的存储单元,因此称它们是形式参数或虚拟参数,简称形参,表示它们并不是实际存在的数据,所以,形参里的变量不能赋值。(使用的时候占用内存,使用完后删除)Main函数中则是等主函数调用完再删除
1)形参不可以赋值
2)在定义函数时指定的形参,必须是,类型+变量的形式
3)// 没形参, 圆括号内容为void关键字 也无返回值
void max(void)
{
}
3.返回值
1)尽量保证return语句中表达式的值和函数返回类型是同一类型。
2)如果不一致,看的是函数返回值得类型
3)也可以没有返回值
3.注意点
1)实参传给形参,而不能由形参传回来给实参。
在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数中实参的值。
(除了穿指针,那么指针中内容会随着改变,因为穿的是地址)
2)如果使用用户自己定义的函数,而该函数与调用它的函数(即主调函数)不在同一文件中,或者函数定义的位置在主调函数之后,则必须在调用此函数之前对被调用的函数作声明。
4.多文件编程
为了避免一个文件被include多次,
五、指针
● 概述
1.内存与外存
内存:RAM(沟通CPU与硬盘得桥梁)
- 暂存放CPU中的运算数据
- 暂存与硬盘等外部存储器交换的数据
外存:ROM
2.物理存储器和存储地址空间
物理存储器:实际存在的具体存储器芯片。(内存条)
存储地址空间:对存储器编码的范围。
1)编码:对每个物理存储单元(一个字节)分配一个号码
2)寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写
3.内存地址:编码就是对内存的每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)
1)char:占一个字节分配一个地址
2)int: 占四个字节分配四个地址
3)float、struct、函数、数组等
4)内存区的每一个字节都有一个编号,这就是“地址”。
4.指针和指针变量
1)指针就是地址,地址就是指针。
●指针基础知识
1.定义和使用
指针变量指向谁,就把谁的地址赋值给指针变量
int *p;
//int *代表是一种数据类型,int*指针类型,p才是变量名
//定义了一个指针类型的变量,可以指向一个int类型变量的地址
int a = 0;
char b = 100;
&可以取得一个变量在内存中的地址。
*操作符操作的是指针变量指向的内存空间
2.可以通过指针间接修改变量的值
3.指针大小
首位的地址(1个地址的4字节或者8字节)---划分的一个小方块
一个地址存储一个字节
p1 p2 p3都是地址,所以一样
4.野指针和空指针
*p = 1000; //操作野指针指向未知区域,内存出问题,err
int *p = NULL; //偶尔用于判断条件
5.万能指针void
void *指针可以指向任意变量的内存空间:
6.const修饰的指针变量
离谁近,修饰谁
●指针和数组
1.数组名:数组的首元素地址,但它是一个常量:
2.用指针操作数组元素 *(a+i)=a[i]
3.指针加减法运算
1)加法
指针计算不是简单的整数相加
如果是一个int *,+1的结果是增加一个int的大小
如果是一个char *,+1的结果是增加一个char大小
相当于转移到一个数的首地址!!! 不然普通的+1 不太行(一个int占用4个地址啊,那想索引下一个Int就需要)
可以通过改变指针指向操作数组元素
2)减法
3)指针数组:数组的每个元素都是指针类型
●多级指针
二级指针:二级指针就是指向一个一级指针变量地址的指针。
●指针与函数
1.指针形参改变实参的值
2.数组名做函数参数
数组名做函数参数,函数的形参会退化为指针:*(此时用sizeof,输出都是4,直接退化为指针,所以必须传递长度,否则无法计算循环)
3.指针为函数的返回值
●指针与字符串
1.字符指针
2.字符指针做函数参数
可以不用数组那样传递长度
因为可以通过\0来判断结束
3.const修饰指针变量
1)离谁近修饰谁
比如const int a=10是无法修改的,可以通过一级指针进行修改
int *p = &a
*p = 100
再比如 const int *p = &a *p值不可以修改
可以通过二级指针**q = &p
**q = 100来修改
4.主函数的形参
用于执行函数时候传递参数 cmd中执行传入
5.项目开发常用字符串应用模型
1)判断字符串出现的次数
2)判断非空字符串个数
总结:
1.p能改 *p就不可修改(*p直接指向一个字符串常量)
●字符串处理函数
1)strcpy()
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:把src所指向的字符串复制到dest所指向的空间中,'\0'也会拷贝过去
2)strncpy()
功能:把src指向字符串的前n个字符复制到dest所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'。
3)strcat()
#include <string.h>
char *strcat(char *dest, const char *src);
功能:将src字符串连接到dest的尾部,‘\0’也会追加过去
4)strncat()
char *strncat(char *dest, const char *src, size_t n);
功能:将src字符串前n个字符连接到dest的尾部,‘\0’也会追加过去
5)strcmp()
int strcmp(const char *s1, const char *s2);
功能:比较 s1 和 s2 的大小,比较的是字符ASCII码大小。
返回值:
相等:0
大于:>0 在不同操作系统strcmp结果会不同 返回ASCII差值
小于:<0
6)strncmp()
int strncmp(const char *s1, const char *s2, size_t n);
功能:比较 s1 和 s2 前n个字符的大小,比较的是字符ASCII码大小。
7) sprintf()
int sprintf(char *str, const char *format, ...);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到出现字符串结束符 '\0' 为止。
8) sscanf()
int sscanf(const char *str, const char *format, ...);
功能:从str指定的字符串读取数据,并根据参数format字符串来转换并格式化数据。
char ch[] = "abc+110=3";
int a, b, c;
char str1[100];
char str2[100];
sscanf(ch, "%x+%o=%d", &a, &b, &c);
a输出为a b输出为b c输出为c
9) strchr()
char *strchr(const char *s, int c);
功能:在字符串s中查找字母c出现的位置
输出为a123abcd
10) strstr()
char *strstr(const char *haystack, const char *needle);
功能:在字符串haystack中查找字符串needle出现的位置
11)strtok()
功能:来将字符串分割成一个个片段。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时, 则会将该字符改为\0 字符,当连续出现多个时只替换第一个为\0。
ps。不局限于特殊字符
第一次截取和后面是不一样的
12)
#include <stdlib.h>
int atoi(const char *nptr);
能:atoi()会扫描nptr字符串,跳过前面的空格字符,直到遇到数字或正负号才开始做转换,而遇到非数字或字符串结束符('\0')才结束转换,并将结果返回返回值。
- atof():把一个小数形式的字符串转化为一个浮点数。
- atol():将一个字符串转化为long类型
指针小节
1.指针数组
----字符串数组 指针数组模型
char *strArray[3] = {"Hello", "World", "of C"};
strArray
是一个包含了 3 个元素的数组,每个元素都是一个指向字符的指针,指向各自字符串的首字符。
2.
字符串是char数组的特例
char str[]="hello world" //字符串
char* p[] = {"hello","world"} //字符串数组
3. 数组作为函数参数 退化为指针丢失精度 需要传递元素个数--非字符串类型,字符串可以通过\0(字符串是一个指针数组模型)
4.
六、内存管理
●作用域
1.局部变量
auto int a = 10; //auto也是局部 可加可不加
//定义变量 局部变量 在函数内部定义的变量-main中使用
//作用域:在函数内部
//生命周期:从创建到函数结束
2.全局变量
//数据区 全局变量可以和局部变量重名
//全局变量 在函数外部定义的变量
//作用域:整个项目中所有文件(所有.c文件) 如果在其他文件中使用 需要声明
//生命周期:从程序创建到程序销毁
1)在函数外定义,可被本文件及其它文件中的函数所共用,若其它文件中的函数调用此变量,须用extern声明
2)全局变量的生命周期和程序运行周期一样
3)不同文件的全局变量不可重名
4)全局变量和局部变量可以重名,数据在操作时候采用最近原则
3.静态局部/全局变量
1)静态局部:只初始化一次,但可以赋值多次
2)静态全局:在函数外定义,作用范围被限制在所定义的文件中
栈区的如果没有初始化,只定义,err
数据区如果没有初始化,默认为0
4.当别的文件使用全局变量,需要进行声明
extern int a;声明一个变量,这个全局变量在别的文件中已经定义了,这里只是声明,而不是定义。
5.全局静态函数
1)一个项目中所有.c文件中的函数(未修饰),可以在整个项目中使用-直接用,所以不可以重名
但是需要进行申明--可以转到定义
2)如果全局函数,c里面不可以名字一样参数个数不一样 c++可以(多态)
3)函数可以调用自己,递归--栈区大小有限
4)全局函数
//全局函数的名称是作用域中唯一的
//作用域:在整个项目中所有文件中使用
//声明周期:从程序创建到程序销毁
5)静态函数 函数名字前加个static
//静态函数可以和全局函数重名,就近原则
//作用域:当前文件中 其他不可以用 定义了也不可以用
//声明周期:从程序创建到程序销毁
-》代码区---唤醒后到栈区
-》所有的函数默认都是全局的,意味着所有的函数都不能重名,但如果是staitc函数,那么作用域是文件级的,所以不同的文件static函数名是可以相同的。
6.内存模型
代码区:共享:代码一样的存储一份,执行程序可以调用它 只读:不修改函数
数据区:--程序同生共死 公共设施 初始化数据区(data段)、 未初始化(bss)、常量:字符串常量 const修饰常量 define修饰的常量
栈区:临时的分配空间 自己加 自己定义(1)局部变量,函数信息,函数参数,数组,指针
(2)栈区大小:1M 太多局部变量就不行
堆区:剩下的空间 酒店
在内存中的地址位置
栈区(stack):地址是从高至低进行的 且地址不会连续 ---安全 有一定间隔
(从高往低进行存储)--刚开始
所以swap里面,先存储了b,再存储了(B地址大)
下到上是出栈,上到下是入栈
大小约为1M
堆区(heap):堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。 连续的空间
int* p = (int *)malloc(sizeof(int)) 四个字节空间 强制类型转换 p是首地址
*p = 123 //使用
free(p)释放堆空间 p野指针 可能报错在使用
p=NULL;
7.内存处理函数
---堆 栈重置值
1) memset()
2) memcpy()
3) memmove()
4) memcmp()
---堆区内存分配和释放
1)malloc()
2)free()
七、复合类型
1.结构体
1)定义和使用
2)结构体成员使用
s1.name(是个地址) s1.age(是个int) 都是可以的
3)结构体数组
结构体赋值,吧struct student看成一个数据类型
4)开辟堆空间存储结构体
typedef struct student ss; //别名struct student的别名
//ss看成一个数据类型
ss * p = (ss *)malloc(sizeof(ss) * 3);//堆区开辟空间
printf("结构体指针大小:%d", sizeof(ss*));//4 因为也是指针的一种 地址
5)结构体嵌套结构体
typedef struct student stu; 不用输入struct studen,用stu代替
6)结构体赋值
struct student stu = { "孙尚香",26,60,"巴蜀" };
struct student s1 = stu; //赋值
s1和stu是独立的空间
7)结构体指针
a.数组结构里面元素是指针
struct student
{
char* name;
int age;
int *scores;
char* addr;
};
需要开辟地址使用
stu.name = (char*)malloc(sizeof(char) * 21);
stu.scores = (int*)malloc(sizeof(int) * 3);
stu.addr = (char*)malloc(sizeof(char) * 51);
strcpy(stu.name, "张三");
stu.age = 18;
stu.scores[0] = 88;
stu.scores[1] = 99;
stu.scores[2] = 100; //scores是指针
strcpy(stu.addr, "北京市");
b.结构体指针 一个指针指向结构体
//结构体指针
struct stu ss = { "林冲",30,100,100,100,"汴京" };
struct stu * p = &ss;//指向起始地址
//printf("%s\n", (*p).name);
//printf("%d\n", (*p).age);
//结构体指针->成员
//结构体变量.成员 区别
printf("%s\n", p->name);
printf("%d\n", p->age);
printf("%d\n", p->scores[0]);
printf("%d\n", p->scores[1]);
printf("%d\n", p->scores[2]);
printf("%s\n", p->addr);
c.上面的结合
而且结构体数组结合
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
typedef struct student ss;
struct student
{
char* name;
int age;
int* scores;
char* addr;
};
int main08()
{
//通过结构体指针操作堆空间
ss* p = (ss*)malloc(sizeof(ss) * 3); //p是指针,然后里面元素也有指针
for (int i = 0; i < 3; i++)
{
//(p + i)->name;
p[i].name = (char*)malloc(sizeof(char) * 21);//p[i]是结构图变量
p[i].scores = (int*)malloc(sizeof(int) * 3);
p[i].addr = (char*)malloc(sizeof(char) * 51);
}
for (int i = 0; i < 3; i++)
{
scanf("%s%d%d%d%d%s", p[i].name, &p[i].age, &p[i].scores[0],
&p[i].scores[1], &p[i].scores[2], p[i].addr);
}
for (int i = 0; i < 3; i++)
{
printf("%s ", p[i].name);
printf("%d ", p[i].age);
printf("%d ", p[i].scores[0]);
printf("%d ", (p + i)->scores[1]);
printf("%d ", (p + i)->scores[2]);
printf("%s\n", (p + i)->addr);
}
//释放堆空间
for (int i = 0; i < 3; i++)
{
free(p[i].name);
free(p[i].scores);
free(p[i].addr);
}
free(p);
system("pause");
return EXIT_SUCCESS;
}
8.结构体和函数
a.结构体普通变量做函数参数
//形参不修改
struct student
{
char name[21];
int age;
int score[3];
char addr[51];
};
void fun01(ss stu1)
{
strcpy(stu1.name, "卢俊义");
printf("%s\n", stu1.name);
}
int main()
{
ss stu = { "宋江",50,101,"水泊梁山" };
fun01(stu);
printf("%s\n", stu.name);
return EXIT_SUCCESS;
}
//形参修改
struct student
{
char *name;
int age;
int score[3];
char addr[51];
};
void fun01(ss stu1)
{
stu1.name = (char*)malloc(sizeof(char)*21); //开辟一个空间 这个时候不可以修改
strcpy(stu1.name, "卢俊义"); //赋值 地址 如果没有重新开辟一个地址就可以修改 同一个地址
printf("%s\n", stu1.name);
}
b.结构体指针变量做函数参数
void fun02(ss * p)
{
strcpy(p->name, "公孙胜");
printf("%s\n", p->name);
}
//可以修改 地址传递
int main0902(void)
{
//结构体指针作为函数参数
ss stu = { "吴用",50,101,"水泊梁山" };
fun02(&stu); //传地址
printf("%s\n", stu.name);
return 0;
}
c.结构体数组名做函数参数
//数组作为函数参数退化为指针 丢失元素精度 需要传递个数
void bubblesort(ss * stu, int len)
{
for (int i = 0; i < len - 1; i++)
for (int j = 0; j < len - i - 1; j++)
{
//if (stu[j].age>stu[j + 1].age)
if ((stu + j)->age > (stu + j + 1)->age)
{
ss temp = stu[j];
stu[j] = stu[j + 1];
stu[j + 1] = temp;
}
}
}
int main(void)
{
ss stu[3] =
{
{ "鲁智深",30,33,33,33,"五台山" },
{"呼延灼",45,44,44,44,"汴京"},
{"顾大嫂",28,33,33,33,"汴京"},
};
//此时stu是数组,所以stu已经是一个地址了
bubblesort(stu, 3);
for (int i = 0; i < 3; i++)
{
printf("姓名:%s\n", stu[i].name);
printf("年龄:%d\n", stu[i].age);
printf("成绩1:%d\n", stu[i].score[0]);
printf("成绩2:%d\n", stu[i].score[1]);
printf("成绩3:%d\n", stu[i].score[2]);
printf("地址:%s\n", stu[i].addr);
}
}
9.const修饰的结构体指针
同上
2.共用体
- 联合union是一个能在同一个存储空间存储不同类型数据的类型;
- 联合体所占的内存长度等于其最长成员的长度倍数,也有叫做共用体;
- 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
- 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
- 共用体变量的地址和它的各成员的地址都是同一地址。
(最大类型的倍数)
(每次一使用一个前面一个都会被覆盖掉)
(他们的地址都是一样的)
3.枚举
switch(type)点击附近可以将case所有的枚举类型用于选择
run如果开始未赋值,就从0开始,后面顺次
4.typedef(取别名,简短)
typedef为C语言的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。
注意点
tip1:数组名是常量,所以需要其他的方式来赋值
tip2:变量值用点,指针用->
(*p).name p->name p是一级指针
tip3:结构体数组、结构体嵌套结构体
tip4:结构体赋值(区别于结构体里面是否元素有指针)
tip5:结构体和指针
(a)指向普通结构体变量的指针
(b)堆区结构体变量(类似于结构体数组)
(c)结构体嵌套以及指针 开辟空间-释放
tip6:结构体做函数参数
(a)结构体普通变量做函数参数--值传递
(b)结构体指针变量做函数参数-地址传递
(c)结构体数组做函数参数--退化为指针,传递长度
tip7:联合体 Union 、枚举enum (switch)、typedef(别名)
八、文件操作
1.文件的打开和关闭
tip:文件的路径必须\\或者/
2.文件字符读写
读) fgetc()
int main0201()
{
FILE* fp = fopen("D:/a.txt", "r");
if (!fp)
{
printf("文件打开失败\n");
return -1;
}
char ch;
//文件的字符读取
//文件默认结尾为-1
ch = fgetc(fp);
printf("%c\n", ch);
//不能修改文件指针 文件在读取时光标流会自动向下移动
//fp++;//err
ch = fgetc(fp); //自动读下一个字符
printf("%c\n", ch);
//关闭文件
fclose(fp);//不能修改文件指针 不然释放出现错误,这个不可以更改
return EXIT_SUCCESS;
}
//可以变为 读取txt文本种所有的内容(包括数字特殊符号中文等等都可以)
int main(void)
{
//FILE* fp = fopen("D:/a.txt", "r");
FILE* fp = fopen("D:/a.txt", "r");
if (!fp)
return -1;
char ch;
while ((ch = fgetc(fp)) != EOF) //!=是7位 =在14位 EOF在系统中定定义为-1
{
printf("%c", ch);
}
fclose(fp);
}
写)fputc()
int main0203(void)
{
//以写的方式打开文件 如果文件不存会创建一个新文件 如果文件存在 会清空内容
FILE* fp = fopen("D:/b.txt", "w");
if (!fp)
return -1;
char ch = 'a';
//字符写入
fputc(ch, fp);
fclose(fp);
return 0;
}
//写成一个.c的代码文件也可以
int main0204(void)
{
FILE* fp = fopen("D:/a.c", "w");
if (!fp)
{
printf("文件打开失败\n");
return -1;
}
char ch;
while (1)
{
scanf("%c", &ch);
if (ch == '@') //代码不用的
{
break;
}
fputc(ch, fp);
}
fclose(fp);
return 0;
}
3.文件加密解密
只正对中文,对于英文数字这个方法没有效果
r只读,w是写
4.文件行读写
1)行读文件 fgets()
int main(void)
{
FILE* fp = fopen("D:/a.txt", "r");
if (!fp)
return -1;
char* p = (char*)malloc(sizeof(char) * 100);
//feof(文件指针) 判断文件是否到结尾 可以判断文本文件也可以判断二进制文件
//如果到文件结尾返回值为 非0的值
//如果没到文件结尾返回值为 0
while (!feof(fp))
{
memset(p, 0, 100);//重置开辟的空间,每次p都是一个干净的缓存区 设置为0 100表示字节数
fgets(p, 100, fp);//出现换行就接受
printf("%s", p);//输出字符串
}
free(p);
fclose(fp);
return 0;
}
2)写 fputs()
int main(void)
{
FILE* fp = fopen("D:/b.txt", "w");
if (!fp)
return -1;
char* p = (char*)malloc(sizeof(char) * 1024);
while (1)
{
memset(p, 0, 1024);//防止字符串污染 将指针 p 指向的内存块设置为零
//scanf("%s", p); //scanf把空格换行都接受表示停止输入 所以最后的结果没有空格和换行
//fgets() //可以接受空格
//接受非换行字符
scanf("%[^\n]", p);//这样做可以允许输入包含空格的字符串,而不仅仅是单个单词。
//吞噬回车\n 不然下一次scanf还会读取缓存种的空格无法继续
getchar();
//停止输入命令 comm=exit
if (!strcmp(p, "comm=exit")) //字符串比较 相等返回一个0
break;
//追加\n
strcat(p, "\n"); \\读完一句需要换行
fputs(p, fp);
}
free(p);
fclose(fp);
}
5.四则运算
enum opt
{
add,sub,mlt,dive
};
int main05()
{
srand((size_t)time(NULL));
FILE* fp = fopen("D:/四则运算.txt", "w");
if (!fp)
return -1;
int a, b;
char c;//+ - * /
char * p = (char*)malloc(sizeof(char) * 20);
for (int i = 0; i < 100; i++)
{
a = rand() % 10 + 1;
b = rand() % 10 + 1;
switch (rand() % 4)
{
case add: c = '+'; break;
case sub: c = '-'; break;
case mlt: c = '*'; break;
case dive: c = '/'; break;
}
memset(p, 0, 20);
sprintf(p, "%d%c%d=\n", a, c, b);
fputs(p, fp);
}
free(p);
fclose(fp);
//p = NULL;
//fp = NULL;
return EXIT_SUCCESS;
}
int main06()
{
FILE* fp1 = fopen("D:/四则运算.txt", "r");
FILE* fp2 = fopen("D:/四则运算结果.txt", "w");
if (!fp1 || !fp2)
{
printf("打开文件失败\n");
return -1;
}
//!feof(fp)文本和二进制文件都可以; EOF -1(文本文件)
int a, b,sum;
char c;
char * p = (char*)malloc(sizeof(char) * 20);
for (int i = 0; i < 100; i++)
{
memset(p, 0, 20);
fgets(p, 20, fp1);
//6*6=\n
sscanf(p, "%d%c%d=\n", &a, &c, &b);
switch (c)
{
case '+':sum = a + b; break;
case '-':sum = a - b; break;
case '*':sum = a * b; break;
case '/':sum = a / b; break;
}
memset(p, 0, 20);
sprintf(p, "%d%c%d=%d\n", a, c, b, sum);
fputs(p, fp2);
}
free(p);
fclose(fp1);
fclose(fp2);
return EXIT_SUCCESS;
}
6.按照格式化文件fprintf fscanf
(类比于sprintf sscanf)
1)读文件fscanf
窗口
文件
读取格式化的内容
//①s说明可以通过格式来读取文件内容
int main0701()
{
FILE* fp = fopen("D:/a.txt", "r");
if (!fp)
return -1;
char* p = (char*)malloc(sizeof(char) * 100);//大的在堆区 char p[1024]在栈区
int a;
fscanf(fp, "%3d", &a); //读出的是数字 3d表示位数
printf("%d\n", a);
//换行、空格 就读取结束
fscanf(fp, "%s", p);//换行空格后的字符
printf("%s", p);
fscanf(fp, "%s", p);
printf("%s", p);
fscanf(fp, "%s", p);
printf("%s", p);
fscanf(fp, "%s", p);
printf("%s", p);
fscanf(fp, "%s", p);//但是这里不是按照行 是字符串
printf("%s", p);
//如果有空格就结束 不断识别是否字符串 是就输出 按照顺序
free(p);
fclose(fp);//无所谓前后
return EXIT_SUCCESS;
}
----------------------------------------------------------------
int main0702(void)
{
FILE* fp = fopen("D:/b.txt", "r");
if (!fp)
return -1;
int a, b, c;
fscanf(fp, "%d+%d=%d", &a, &b, &c);
按照格式读取字符串并赋值
------------------------------------------------------------------
//当文本里面是hello world 空格输出fscanf只剩下hello
// 字符内数组,不是字符串
//char ch[20] = {0};//字符需要初始化,才有\0
//memset(ch, 0, 20);//或者
//fscanf(fp, "%[^\n]", ch);//hello world 除了\n外的所有
//fscanf(fp, "%11c", ch);//hello world --必须初始化
//fscanf(fp, "%3c", ch);//hel
//fscanf(fp, "%3s", ch);//hel
2)写 fprintf
//写
int main0704(void)
{
srand((size_t)time(NULL));
FILE* fp = fopen("D:/四则运算.txt", "w");
if (!fp)
return -1;
int a, b;
char c;
for (int i = 0; i < 100; i++)
{
a = rand() % 10 + 1;
b = rand() % 10 + 1;
switch (rand()%4)
{
case 0:c = '+'; break;
case 1:c = '-'; break;
case 2:c = '*'; break;
case 3:c = '/'; break;
}
fprintf(fp, "%d%c%d=\n", a, c, b);
}
fclose(fp);
}
int main0705(void)
{
FILE* fp1 = fopen("D:/四则运算.txt", "r");
FILE* fp2 = fopen("D:/四则运算结果.txt", "w");
if (!fp1 || !fp2)
{
printf("打开文件失败\n");
return -1;
}
int a, b,sum;
char c;
for (int i = 0; i < 100; i++)
{
fscanf(fp1, "%d%c%d=\n", &a, &c, &b);//读出
switch (c)
{
case '+':sum = a + b; break;
case '-':sum = a - b; break;
case '*':sum = a * b; break;
case '/':sum = a / b; break;
}
fprintf(fp2, "%d%c%d=%d\n", a, c, b, sum);//写进去
}
fclose(fp1);
fclose(fp2);
}
7.大文件排序
//生成1000个0-255的随机数,并写入文本
int main08()
{
srand((size_t)time(NULL));
FILE* fp = fopen("D:/数据.txt", "w");
if (!fp)
return -1;
for (int i = 0; i < 1000; i++)
{
fprintf(fp, "%d\n", rand() % 256); //-0-255
}
fclose(fp);
return EXIT_SUCCESS;
}
//使用冒泡排序(次数多)
int main0901()
{
FILE* fp1 = fopen("D:/数据.txt", "r");
FILE* fp2 = fopen("D:/数据冒泡版排序.txt", "w");
if (!fp1 || !fp2)
return -1;
//冒泡版
int * arr = (int*)malloc(sizeof(int) * SIZE);//字节为单位
for (int i = 0; i < SIZE; i++)
{
fscanf(fp1, "%d\n", &arr[i]);//读
}
BubbleSort(arr, SIZE);
for (int i = 0; i < SIZE; i++)
{
fprintf(fp2, "%d\n", arr[i]);//写
}
free(arr);
fclose(fp1);
fclose(fp2);
return EXIT_SUCCESS;
}
/桶排序-适用于一种连续的数据范围,多次出现
int main09(void)
{
FILE* fp1 = fopen("D:/数据.txt", "r");
FILE* fp2 = fopen("D:/数据插入版排序.txt", "w");
if (!fp1 || !fp2)
return -1;
int* arr = (int*)malloc(sizeof(int) * 256);
memset(arr, 0, sizeof(int)*256);//单位字节
for (int i = 0; i < 1000; i++)
{
int value;
fscanf(fp1, "%d\n", &value);
arr[value]++;//将数据的个数放在对应的下标里面
}
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < arr[i]; j++)
{
fprintf(fp2, "%d\n", i);
}
}
free(arr);
fclose(fp1);
fclose(fp2);
return 0;
}
8.文件块读写(基于二进制)
//写
int main1001()
{
FILE* fp = fopen("D:/c.txt", "wb");
if (!fp)
return -1;
//int a = 5678;
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
fwrite(arr, sizeof(int), 10, fp);
fclose(fp);
return EXIT_SUCCESS;
}
//读
int main1002(void)
{
FILE* fp = fopen("D:/c.txt", "rb");
if (!fp)
return -1;
//int value;
//fread(&value, sizeof(int), 1, fp);
int arr[10] = { 0 };
fread(arr, sizeof(int), 10, fp);
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
fclose(fp);
return 0;
}
总结:
1.本文文件换个二进制文件
2.文件打开fopen() r,w,a 读写追加
文件关闭fclose()
3.文件得顺序读写
1)按照字符 写fputc() 读fgetc()
本文文件结尾EOF(-1)为结尾
feof()文本文件和二进制都可以 非0已经到末尾 0没有到文件末尾
2)按照行
写fputs()--遇到\0结尾 字符串结束
读fgets() 读存一个\0 预留
3)字符串读写格式化
写fprintf() 读fscanf()
4)文件块读写(二进制)