C语言 笔记

一、初识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与硬盘得桥梁)

  1. 暂存放CPU中的运算数据
  2. 暂存与硬盘等外部存储器交换的数据

外存: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);

功能:比较 s1s2 的大小比较的是字符ASCII码大小

返回值

       相等:0

       大于:>0 在不同操作系统strcmp结果会不同   返回ASCII差值

       小于:<0

6)strncmp()

int strncmp(const char *s1, const char *s2, size_t n);

功能:比较 s1s2 前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')才结束转换,并将结果返回返回值。

  1. atof():把一个小数形式的字符串转化为一个浮点数。
  2. 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.共用体

  1. 联合union是一个能在同一个存储空间存储不同类型数据的类型;
  2. 联合体所占的内存长度等于其最长成员的长度倍数,也有叫做共用体;
  3. 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
  4. 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
  5. 共用体变量的地址和它的各成员的地址都是同一地址。

(最大类型的倍数)

(每次一使用一个前面一个都会被覆盖掉)

(他们的地址都是一样的)

 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)文件块读写(二进制)

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值