C语言笔记终极版
前言
接着上次笔记,继续更新笔记。如果您打开了我文章,希望您能看完,有不对或者可以改进的地方,留言在下面,非常感谢。
文章目录
一 C语言文件操作
1.1什么是文件?
-
文件有不同的类型,在程序设计中,主要用到两种文件:
(1)程序文件。包括源程序文件(后缀名为.c),目标文件(后缀名为.obj),可执行文件(后缀名为.exe)等。这一类型的文件主要用于存储程序代码。
(2)数据文件。此文件的内容不是程序,而是程序运行时读写的数据,比如程序运行过程中输出到磁盘或其他设备上的数据,或在程序运行过程*程 -
序读取的数据。
硬件被看成一个文件
操作文件的正确流程为:打开文件 --> 读写文件 --> 关闭文件。
1.2文件的概念
- 文件名
文件标识也称为文件名 - 文件的分类
根据数据的组织形式,数据文件可分为ASCII文件和二进制文件。 - 文件存储方法的区别
一个数据在磁盘上存储,字符一律以ASCII形式存储,数值型既可以用ASCII形式存储也可以用二进制形式存储。二进制形式存储就相当于直接把内存中的内容原封不动存储在磁盘上,由于不需要转换,所以二进制文件更加方便计算机处理。
1.3指向文件的指针
1.3.1文件指针的定义
1.文件指针的定义格式为:
FILE *fp;
定义一个指针fp用于指向FILE类型的数据。
1.3.2打开与关闭文件
1.打开文件
例如:FILE *fp;//定义一个文件指针fp
fp=fopen(“text.txt”,”r”);//使fp指向文件“text1.txt”的首地址
2.文件的打开方式3.关闭文件
fclose函数的一般形式为:
fclose(文件指针);
例如:fclose(fp);
在每次程序终止之前都要养成习惯关闭所有的文件。
1.3.3顺序读写文件
1.字符输入和输出函数:
使用字符读取函数fgetc从文件读取一个字符
ch=fgetc(fp);
使用字符写入函数fputc向文件写入一个字符
fputc(ch,fp);
2.字符串输入和输出函数
使用字符串读取函数fgets从文件读取一个字符串
fgets(str,n,fp);
//从文件指针fp指向的位置读取一个长度位n-1的字符串(最后一位赋值‘\0’,用作字符串结束标志),存放在字符数组str中。
使用字符串写入函数fputs向文件写入一个字符串
fputs(str,fp);
//把str所指向的字符串写入文件指针fp指向的位置,
3.格式化输入和输出函数
使用格式化输出函数fprintf向文件写入数据
fprintf(文件指针,“格式化字符串”,输出列表);
例如:fprintf(fp,“%d,%c”,x,y);
//将变量x以整型的形式写入文件指针fp指向的位置,把变量y以字符的形式写入文件指针fp指向的位置。
使用格式化输入函数fscanf从文件读取数据
fscanf(文件指针,“格式化字符串”,输入列表);
例如:fscanf(fp,“%d,%f”,&x,&y);
//从文件指针fp指向的位置读入一个整型数据和一个单精度型数据,分别存入变量x和变量y中
4.以二进制的形式读写数据
fread(buffer,size,count,fp);
//buffer是一个地址(数组),用于存储从文件读取出来的数据,size为需要读取的字节数,count为需要读取数据项的个数(每个数据项的大小为size)。
fwrite(buffer,size,count,fp);
1.3.4任意位置读写文件
1.文件指针位置标记及定位
使用rewind函数强制使文件指针fp指向文件开头的位置。
rewind(fp);
使用fseek函数使文件指针指向文件中任意位置。
fseek(fp,位移量,起始点);
//起始点用0、1、2代替,0代表文件开始位置,1代表当前位置,2代表文件末尾位置。
//位移量是指以起始点为基础,向前移动的字节数。
例如:
fseek(fp,100,0);//将文件指针fp向前移动到离文件开头100个字节处;
fseek(fp,50,1);//将文件指针fp向前移动到离当前位置50个字节处;
fseek(fp,-10,2);//将文件指针fp向后移动到离文件末尾10个字节处;
fseek一般用于二进制文件。
用rewind和fseek函数实现随机读写。
1.4文件读写的出错检测
ferror(fp);
if(ferror(fp))
{
printf(“文件读写失败!”);
二 指针
2.1什么是指针?
计算机中所有的数据都必须放在内存中。为了访问这些数据,必须为每个字节都编上号码,每个字节的编号是唯一的,根据编号可以准确地找到某个字节。内存中字节的编号称为地址或指针.
它就相当于旅馆中的房间号,每一个房间都可以看作一块内存区域,都可以用来存放东西,我们给每个房间都编一个房间门牌号,用于更好的区分每一个房间,内存中也是一样的,整个内存由很多个字节组成,每个字节都有其对应的“房间号”,这就是“地址”了。通过这个“房间号”就可以找到其对应的“房间”,然后就可以从房间里取东西,或者把东西放进房间里了。
2.2指针常量与指针变量
2.2.1指针常量
1.指针常量
指针常量就是指针的指向不能够被改变的指针
int * const p;
2.常量指针
常量指针就是指向常量的指针
const int *p;
2.2.2指针变量
类型名 * 指针变量名;
例如:int *p,*q; char *p1,*q1; double *p2,*q*2;
int a=3,b;//这是一个普通的整形变量
int *p;//这是一个整形的指针
//a的地址是0x2000,p的地址是0x3000
1.指针变量的引用
如:int a = 10,b=20;
int *p=&a;
//定义一个整型指针变量p,初始化p的值为a的地址,也就是p指向a地址
*p=30; //通过指针变量p引用a变量,改变a的值为30
//这里的’*’为解引用符号,*p引用指针变量p所指向地址中对应的值
printf(“%d\n”,*p); //通过指针变量p输出变量a的值
p=&b; //改变指针p的指向,指针p不再指向a的地址了,而是指向b的地址
printf(“%d\n”,*p); //输出变量b的值
2.输出内存地址编号
printf(“%p\n”,p); //以十六进制的格式输出指针变量p所指向地址的内存地址编号
printf(“%d\n”,&a); //以十进制的格式输出变量a所在的内存地址编号
printf(“%o\n”,&b); //以八进制的格式输出变量b所在的内存地址编号
printf(“%p\n”,&p); //以十六进制的格式输出指针变量p所在的内存地址编号
3.多重指针的使用
int a = 10,*p,**q,**r; //定义整型变量a、指针p、双重指针q、三重指针r
就可以有以下赋值语句:
p=&a; //使一级指针p指向变量a的地址
q=&p; //使双重指针q指向一级指针p的地址
r=&q; //使三重指针r指向双重指针q的地址
*p=20; //使用一级指针p给变量a赋值
**q=30; //使用二级指针q给变量a赋值
***r=40; //使用三级指针r给变量a赋值
2.3指针变量作为函数参数
1.函数参数为指针类型的函数
void fun1(int x, int y) //普通的函数fun1(形参)
{
printf("x1=%d\ty1=%d\n", x++, y++);
printf("x2=%d\ty2=%d\n", x, y);
return x;
}
void fun2(int *x, int *y) //形参为指针类型的函数fun1(*形参)
{
printf("x3=%d\ty3=%d\n", (*x)++, (*y)++);
printf("x4=%d\ty4=%d\n", *x, *y);
}
int main()
{
int n =10, m =20;
fun1(n,m); //fun1(实参)
printf("n1=%d\tm1=%d\n",n,m);
fun2(&n, &m); //fun2();
printf("n2=%d\tm2=%d\n",n,m);
return 0;
}
2.打印结果
2.4通过指针引用数组
1.数组元素的指针
数组元素的地址表示:
如:int a[10],*p;
&a[0]; //引用数组元素a[0]地址的表示方法
2.指针指向数组元素
p=&a[1]; //指针变量p指向数组元素a[1]的地址
3.指针指向的移动
p=&a[0];
*p++; //指针指向地址中的数值加1
printf(“%#p\n”,p); //打印指针变量p所指向的地址编号
p++; //指针移动到数组元素a[1]的位置
printf(“%#X\n”,p); //打印移动后指针变量p所指向的地址编号
//指针变量++(或--)移动一次是移动其基类型大小的内存区域
2.5指向函数的指针(函数指针)
1.函数指针的初始化及使用
int funsum(int x,int y)
{ //求和函数
return x+y;
}
int funmax(int x,int y)
{ //求最大值函数
return x>y?x:y;
}
int funmin(int x,int y)
{ //求最小值函数
return x<y?x:y;
}
int main()
{
int (*funp)(int,int)=funsum; //定义一个函数指针funp,初始化赋值指向函数funsum
int a=10,b=20,c; //函数指针有以下两个赋值方式和两种调用方式
c=(*funp)(a,b); //通过函数指针funp调用函数funsum,(*)区分funp是个函数指针
funp=&funmax; //改变函数指针funp的指向,&取函数地址,使其指向函数funmax
c=funp(a,b); //此时是通过函数指针funp调用函数funmax
funp=funmin; //改变函数指针funp的指向,可以省略&取址符,使其指向函数funmin
c=funp(a,b); //通过函数指针funp调用函数funmin
return 0;
}
所谓的指针函数,其本质上是个函数,是返回值为指针类型的函数
所谓的函数指针,其本质上是个指针,是指向函数的指针
2.6指针数组和数组指针
2.6.1指针数组
定义的一般格式:数据类型 *指针数组名[数组元素个数];
如:int *p[6]; //定义一个指针数组,有6个元素,分别可以指向六个地址
1.使用指针数组指向二维字符数组
char arr[10][10],*p[10];
for(int i=0;i<10;i++)
{
p[i]=arr[i]; //指针数组p中的每个元素指向二维数组arr的每一行
}
for(int i=0;i<10;i++)
{
scanf(“%s”,p[i]); //使用指针数组p给二维数组arr赋值
}
for(int i=0;i<10;i++)
{
printf(“%s”,p[i]); //使用指针数组p输出二维数组arr
}
2.6.2数组指针
1.数组指针的定义
定义的一般格式:数据类型 (*指针变量名)[所指向数组的大小];
如:int a[3][4];
int (*p)[4]; //定义一个整型数组指针p,用于指向大小为4的整型数组
p=a; //将整型数组指针p指向二维数组a的第一行
2.使用数组指针完成二维数组的输入和输出
int a[3][6];
int (*p)[6];
p = a;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 6;j++)
{
scanf("%d",&p[i][j]);
}
for (int i = 0; i < 3; i++)
for (int j = 0; j < 6;j++)
{
printf("%d\t",p[i][j]);
}
2.7结构体指针
1.结构体指针的定义
struct student
{
int id;
char *name;
char sex[4];
float score;
}s1,s2,s3;
//可以定义指向struct student类型结构体的指针:
struct student *sp;
sp = &s1;//用struct student类型的结构体指针sp指向struct student类型的结构体变量s1
2.通过结构体指针引用结构体变量及其结构体成员
//指向结构体成员运算符:’->’
如:sp->id=100; //通过结构体指针引用结构体成员用指向结构体成员运算符’->’
printf(“%s”,sp->name);
2.8内存四区
在系统为程序开辟内存时,将内存区域划分为4块,分别为:
栈区:存放局部变量,由系统自动申请和释放
堆区:动态分配内存区域,由自己手动申请和释放,用到指针管理
全局静态常量区:存放常量(一般是字符串常量),全局变量和静态变量
代码区:存放可执行的代码
三 总结
虽然这些都是基础,但我们要是把这些最基础的搞明白了,后面就很轻松了。对于这次总结的也是重难点,希望对我们都有帮助。