秘书部第三次培训总结
文章目录
指针复习
指针变量:一类通过地址访问指定内存存储单元的变量
1、三个符号& * +
& 用于取一般变量的地址, * 用于取指针变量的值
int a; //定义一个一般变量a
int* p; //定义一个指针变量p
p = &a; //使用&取一般变量a的地址,并赋给指针变量p
printf("%d\n",*p);//使用*取指针变量的值,并输出
指针是一个地址,指针变量的自加和自减不是简单的在地址上的加1,而是获得下一个数据的地址,已知数组的名称就是该数组首元素的地址 ,那么数组名称加一就是第二个元素的地址。
int *p, a[5], i = 0;
p = a; //p获取到数组a的首地址
printf("please input array a:\n");
for (i = 0; i < 5; i++) //循环输入数组
{
scanf("%d", p); //把键盘输入的整型传入p(一个地址)
p++; //让p指向下一个元素
}
p = a; //重新让p获取到数组a的首地址
printf("array a is:\n");
for (i = 0; i < 5; i++)
{
printf("%5d", *p); //输出地址p中的整型内容
p++; //让p指向下一个元素
}
printf("\n");
2、一个函数
C语言中函数一般是值传递,所以如果想把交换大小的代码用函数封装起来是没有用的
#include <stdio.h>
void swap(int i, int j);
int main()
{
int x = 1, y = 7;
printf("在函数外,交换前的数据:%d\t%d\n", x, y);
swap(x, y);
printf("在函数外,交换后的数据:%d\t%d\n", x, y);
return 0;
}
void swap(int i, int j)
{
printf("在函数中,交换前的数据:%d\t%d\n", i, j);
int temp = 0;
temp = i;
i = j;
j = temp;
printf("在函数中,交换后的数据:%d\t%d\n", i, j);
}
结果如图:
原理:函数swap在调用时会自动创建两个局部变量i,j并把参数x,y的值赋值给i,j,局部变量i,j只在函数体内有效,函数使用结束时会自动消除,在函数体的范围内i,j的值确实发生了交换,但是对于x和y,只是用到了他们的值,而没有对他们进行操作,自然也就不能改变x和y的值。
解决方法:地址传递
为了实现数值改变的封装函数,我们可以采用地址传递的方法(PS:注意这里不是引用传递)
我们选择把数据的地址传入函数,通过地址修改地址中内容
#include <stdio.h>
void swap(int* i, int* j); //函数参数为指针变量
int main()
{
int x = 1, y = 7;
printf("在函数外,交换前的数据:%d\t%d\n", x, y);
swap(&x, &y); //使用取地址符号&获取x和y的地址,并传入函数
printf("在函数外,交换后的数据:%d\t%d\n", x, y);
return 0;
}
void swap(int* i, int* j)
{
printf("在函数中,交换前的数据:%d\t%d\n", *i, *j);
int temp = 0;
temp = *i; //通过取内容符号*获取地址中的内容,并进行修改
*i = *j;
*j = temp;
printf("在函数中,交换后的数据:%d\t%d\n", *i, *j);
}
结果如图:
原理:函数开始调用时同样创建了局部指针变量i、j并且把传入参数的地址赋值给这两个指针变量,这样在函数体里就可以通过地址修改x和y的内容
结构体
“结构体”是一种构造类型。
构造类型:并不是C语言官方的,而是认为定义的只在一定范围内有效的数据类型。
1、结构体的概念
作为一种构造类型,结构体由若干“成员”组成,其中成员可以是基本数据类型或者是构造类型。
一般形式:
struct 结构体名
{
成员列表
};
结构体作为一种类型体现了“ 封装 ”的概念,将一种事物的几种属性封装到一起来表示这一种事物,比如说我们可以将“姓名”,“学号”,“成绩”几个属性封装到一起组成结构体学生作为数据类型学生。
struct Student
{
char cName[20]; //字符串类型的学生姓名
int iNo; //整型的学生学号
float fScore; //浮点型的学生成绩
};
2、结构体的定义和初始化
声明一个结构体表示的是创建一中新的类型名,要用新的类型名载定义变量,这里我们介绍两种定义方式。
第一种:我们使用上面创建过的学生类来定义两个学生stu1和stu2。
struct Student stu1;
struct Student stu2;
第二种:在声明结构类型时,可以同时定义变量
struct Student
{
char cName[20];
int iNo;
float fScore;
}stu3,stu4;
这样stu1、stu2、stu3、stu4就可以被使用了,他们四个都有各自的cName
、iNo、fScore属性。
与此同时:结构体数组也可以以相同的方式构造:
#include <stdio.h>
struct Student
{
char cName[20];
int iNo;
float fScore;
}stu5[20]; //以第二种方式创建大小为20个的struct Student类型数组stu5
int main()
{
struct Student stu6[20];//以第一种方法创建大小为20个的struct Student类型数组stu6
return 0;
}
3、结构体的使用
定义结构体类型变量以后,就可以调用和操作结构体变量内部的成员,不能对结构体整体进行操作,结构体变量成员的一般形式为:结构体变量名.成员名
在结构体变量名后面加上成员运算符“ . ”和成员名字:
#include <stdio.h>
#include <string.h>
struct Student
{
char cName[20];
int iNo;
float fScore;
}
int main()
{
struct Student stu1;
strcpy(stu1.cName,"FirStu");
stu1.fScore = 100;
scanf("%d",&stu1.iNo);
stu1.iNo++;
printf("%s %d %f",stu1.cName,stu1.iNo,stu1.fScore);
return 0;
}
4、结构体指针
字面理解,即指向结构体变量起始位置的指针,既然指针指向结构体变量的地址,因此可以使用结构体指针来访问结构体中的成员。
定义结构体的一般形式:结构体类型 *指针名;
#include <stdio.h>
struct Student
{
char cName[20];
int iNo;
float fScore;
}
int main()
{
struct Student * stu1;
return 0;
}
结构体指针有两种方式可以对其中的成员进行引用:
第一种:使用 * 对结构体指针取内容,并使用成员运算符 .
第二种:直接通过箭头 -> 调用,结构体指针名->成员名
struct Student * stu1;
(*stu1).iNo = 11;
stu1->fScore = 120;
结构体指针的举例说明:
#include <stdio.h>
#include <string.h>
struct Student
{
char cName[20];
int iNo;
float fScore;
};
int main()
{
struct Student * pStu;
struct Student stu;
pStu = &stu;
strcpy(pStu->cName, "FirStu");
pStu->iNo = 2018;
pStu->fScore = 120;
(*pStu).fScore++;
printf("-----the student's information ----\n");
printf("Name:%s\n", stu.cName);
printf("No:%d\n", stu.iNo);
printf("Score:%f\n", stu.fScore);
return 0;
}
运行结果:
链表
链表,一种有关线性表的链式存储户结构,一种常见的数据结构
1、链表的概念
链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
C语言中,我们为了把数据和指针封装到一个整体,采用结构体,每一个节点是一个结构体,一个结构体中包括数据和指向下一个结构体的指针。一般来说,这里的一条数据链上的结构体类型都是一致的。
下面我们使用C语言创建一个这样的结构体
struct Student
{
char cName[20];
int iNo;
struct Student * pNext;
}
这里的pNext就是一个指针,它可以指向一个struct Student类型。
2、动态链表的生成
使用函数动态生成一个链表,并返回链表头。
这个函数利用键盘输入链表节点数据,并且设定当输入的学号iNo为0时结束链表的生成。
/*
==========================
功能:创建n个节点的链表
返回:指向链表表头的指针
==========================
*/
struct Student *Create()
{
struct Student *pHead = NULL; //头节点
struct Student *p1 = NULL; //p1保存创建的新节点的地址
struct Student *p2 = NULL; //p2保存原链表最后一个节点的地址
iCount = 0; //创建前链表的节点总数为0:空链表
p1 = (struct Student *) malloc(sizeof(struct Student)); //开辟一个新节点
p2 = p1; //如果节点开辟成功,则p2先把它的指针保存下来以备后用
if (p1 == NULL) //节点开辟不成功
{
printf("\nCann't create it, try it again in a moment!\n");
return NULL;
}
else //节点开辟成功
{
printf("Please input %d node -- iNo,fScore: ", iCount + 1);
scanf("%d %f", &(p1->iNo), &(p1->fScore)); //录入数据
}
while (p1->iNo != 0) //只要学号不为0,就继续录入下一个节点
{
iCount += 1; //节点总数增加1个
if (iCount == 1) //如果节点总数是1,则pHead指向刚创建的节点p1
{
pHead = p1;
p2->pNext = NULL; //此时的p2就是p1,也就是p1->next指向NULL。
}
else
{
p2->pNext = p1; //指向上次下面刚刚开辟的新节点
}
p2 = p1; //把p1的地址给p2保留,然后p1产生新的节点
p1 = (struct Student *) malloc(sizeof(struct Student));
printf("Please input %d node -- iNo,fScore: ", iCount + 1);
scanf("%d %f", &(p1->iNo), &(p1->fScore));
}
p2->pNext = NULL; //此句就是根据单向链表的最后一个节点要指向NULL
free(p1); //p1->iNo为0的时候跳出了while循环,并且释放p1
p1 = NULL; //特别不要忘记把释放的变量清空置为NULL,否则就变成"野指针",即地址不确定的指针
return pHead; //返回创建链表的头指针
}
3、链表输出
使用函数将以head为链表头的链表输出出来
/*
===========================
功能:输出节点
返回: void
===========================
*/
void Print(struct Student *head)
{
struct Student *p;
printf("\nNow , These %d records are:\n", iCount);
p = head;
if (head != NULL) //只要不是空链表,就输出链表中所有节点
{
printf("head is %o\n", head); //输出头指针指向的地址
while (p != NULL)
{
/*
输出相应的值:当前节点地址、各字段值、当前节点的下一节点地址。
这样输出便于读者形象看到一个单向链表在计算机中的存储结构,和我们
设计的图示是一模一样的。
*/
printf("%o %d %5.1f %o\n", p, p->iNo, p->fScore, p->pNext);
p = p->pNext;
}
}
}
4、链表中指定节点的删除
删除指定链表在指定位置的节点
/*
==========================
功能:删除指定节点
(此例中是删除指定学号的节点)
返回:指向链表表头的指针
==========================
*/
struct Student *Del(struct Student *head, int no)
{
struct Student *p1; //p1保存当前需要检查的节点的地址
struct Student *p2; //p2保存当前检查过的节点的地址
if (head == NULL) //是空链表
{
printf("\nList is null!\n");
return head;
}
//定位要删除的节点
p1 = head;
while (p1->iNo != no && p1->pNext != NULL)//p1指向的节点不是所要查找的,并且它不是最后一个节点,就继续往下找
{
p2 = p1; //保存当前节点的地址
p1 = p1->pNext; //后移一个节点
}
if (p1->iNo == no) //找到了
{
if (p1 == head) //如果要删除的节点是第一个节点
{
head = p1->pNext;//头指针指向第一个节点的后一个节点,也就是第二个节点。这样第一个节点就不在链表中,即删除
}
else //如果是其它节点,则让原来指向当前节点的指针,指向它的下一个节点,完成删除
{
p2->pNext = p1->pNext;
}
free(p1); //释放当前节点
p1 = NULL;
printf("\ndelete %ld success!\n", no);
iCount -= 1; //节点总数减1个
}
else //没有找到
{
printf("\n%ld not been found!\n", no);
}
return head;
}
5、链表中在指定位置插入节点
调用函数,在链表的指定位置插入节点
/*
==========================
功能:插入指定节点的后面
(此例中是指定学号的节点)
返回:指向链表表头的指针
==========================
*/
struct Student *Insert(struct Student *head, int no, struct Student *node)
{
if (head == NULL)
{
head = node;
node->pNext = NULL;
iCount += 1;
return head;
}
struct Student *p1 = head; //p1保存当前需要检查的节点的地址
while (p1->iNo != no && p1->pNext != NULL)//p1指向的节点不是所要查找的,并且它不是最后一个节点,继续往下找
{
p1 = p1->pNext; //后移一个节点
}
if (p1->iNo == no) //找到了
{
node->pNext = p1->pNext;//显然node的下一节点是原p1的next
p1->pNext = node; //插入后,原p1的下一节点就是要插入的node
iCount += 1; //节点总数增加1个
}
else
{
printf("\n%ld not been found!\n", no);
}
return head;
}
文件
“文件”是指一组相关数据的有序集合。这个数据集有一个名称,叫做文件名。
文件指针是一个西乡文件有关信息的指针,这些信息包括文件名,状态和当前位置。使用文件时需要在内存中为其分配空间,用来存放文件的基本信息。
1、文件的打开、关闭与操作安全
定义一个文件指针:FILE * file;
打开文件函数:fopen(文件路径,使用文件方式)
关闭文件函数:fclose(文件名)
C语言中对于文件的使用方式要细分为“读”,“写”,“追加”
“读”:指的是从文件头开始读取数据;
“写”:指的是从文件头开始写入数据,文件种原来的数据会被覆盖;
“追加”:指的是从文件尾部写入数据,文件中原来的数据会被保留;
而且由于C语言中打开的文件分为“文本文件”和“二进制文件”,所以不同的文件类型对应的同一种文件打开方式的名称不同。
文件打开的代码举例:
FILE * file;
file = fopen("E://my//123.txt","w"); //使用只写方式打开E盘下的my文件夹下的123.txt文件
//......文件相关操作
fclose(file);
file = fopen("E://my//123.txt","a"); //使用只写方式打开E盘下的my文件夹下的123.txt文件
//......文件相关操作
close(file);
文件打开和关闭的安全问题
对于fopen函数,如果打开成功,返回文件指针,如果打开失败,返回NULL空针;
对于fclose函数,如果关闭成功,返回整型0,如果关闭失败,返回EOF;
可以通过这两个函数返回值得特性来确保文件打开与关闭的安全性,代码示例:
#include <stdio.h>
int main()
{
FILE * file;
if((file = fopen("E://my//123.txt","w")) == NULL)
{
printf("文件打开失败!\n");
exit(0); //安全退出函数
}
//......文件打开已成功......文件操作
if(fclose(file) == EOF)
{
printf("文件关闭失败");
exit(0); //安全退出函数
}
return 0;
}
2、文件的fprintf写入和fscanf读取
C语言中,printf是把数据输入到缓冲区,scanf为把缓冲区数据读出,
则,fprintf是把数据输入到文件流,fscanf把文件流中的数据读出。
函数使用:
fprintf(文件指针,格式字符串,输出列表),fscanf(文件指针,格式字符串,输出列表)
代码示例:
fprintf的使用
#include <stdio.h>
int main()
{
FILE * file;
char ch;
if((file = fopen("E://my//123.txt","w")) == NULL)
{
printf("文件打开失败!\n");
exit(0); //安全退出函数
}
//===========fprintf==========
while (scanf("%c", &ch) != EOF) //从键盘输入字符
{
if (ch == '#') { break; } //如果输入字符#跳出while循环
fprintf(file, "%c", ch); //将输入的字符输出到file文件指针所指向的文件
}
if(fclose(file) == EOF)
{
printf("文件关闭失败");
exit(0); //安全退出函数
}
return 0;
}
fscanf的使用
#include <stdio.h>
int main()
{
FILE * file;
char ch;
if((file = fopen("E://my//123.txt","w")) == NULL)
{
printf("文件打开失败!\n");
exit(0); //安全退出函数
}
//===========fscanf==========
while (fscanf(file, "%c", &ch) != EOF) //从file文件依次读出字符到ch,指导文件结束
{
printf("%c", ch);
}
if(fclose(file) == EOF)
{
printf("文件关闭失败");
exit(0); //安全退出函数
}
return 0;
}
对于fscanf函数,只有读取到文件末尾的时候,会返回EOF,可以通着这个返回值得特性判断文件读取是否结束
3、文件的fputc写入和fgetc读取
fputc函数的一般形式: fputc(ch,file),此时文件应该是以“读”或者“只读”的方式打开
可以把一个字符写到文件指针file所指的文件中,其中ch是要输出的字符,如果输出成功返回输出的字符,如果失败,返回EOF
fgetc函数的一般形式:ch = fgetc(file),此时文件应该是以“写”或者“只写”的方式打开
从文件指针file所指的文件中读取一个字符赋给ch,此函数当遇到文件结束符时返回EOF
fputc
#include <stdio.h>
int main()
{
FILE * file;
char ch;
if((file = fopen("E://my//123.txt","w")) == NULL)
{
printf("文件打开失败!\n");
exit(0); //安全退出函数
}
//===========fputc==========
while (scanf("%c", &ch) != EOF) //从键盘输入字符
{
if (ch == '#') { break; } //如果输入字符#跳出while循环
fputc(ch,file); //将输入的字符输出到file文件指针所指向的文件
}
if(fclose(file) == EOF)
{
printf("文件关闭失败");
exit(0); //安全退出函数
}
return 0;
}
fgetc
#include <stdio.h>
int main()
{
FILE * file;
char ch;
if((file = fopen("E://my//123.txt","w")) == NULL)
{
printf("文件打开失败!\n");
exit(0); //安全退出函数
}
//===========fgets==========
while ((ch=fgetc(file)) != EOF) //从file文件依次读出字符到ch,指导文件结束
{
printf("%c", ch);
}
if(fclose(file) == EOF)
{
printf("文件关闭失败");
exit(0); //安全退出函数
}
return 0;
}
4、文件的fputs写入和fgets读取
fputs函数和fputc函数相类似,区别在于,fputc每次只能项文件中写入一个字符,二fputs每次项文件中写入一个字符串
fputs函数的一般形式:fputs(字符串,文件指针)
fputs
#include <stdio.h>
int main()
{
FILE * file;
char ch[10] = {'a','b','c','d'};
if((file = fopen("E://my//123.txt","w")) == NULL)
{
printf("文件打开失败!\n");
exit(0); //安全退出函数
}
//===========fputs==========
fputs(ch,file);
if(fclose(file) == EOF)
{
printf("文件关闭失败");
exit(0); //安全退出函数
}
return 0;
}
fgets
#include <stdio.h>
int main()
{
FILE * file;
char ch[10];
if((file = fopen("E://my//123.txt","w")) == NULL)
{
printf("文件打开失败!\n");
exit(0); //安全退出函数
}
//===========fgets==========
fgets(file,ch);
if(fclose(file) == EOF)
{
printf("文件关闭失败");
exit(0); //安全退出函数
}
return 0;
}