A:关于输入和输出
先输出“hello word”回忆初学时的心情!
#include<stdio.h>
int main()
{
printf("hello word");
getch();
return 0;
}
输入和输出时要用到两个语句
Scanf(“”, );或scanf(“ ”);
Printf(“”, );或printf(“ ”);
在进行输入输出时主要会发生一下错误。
(1) 格式对应
例1:定义浮点型变量double a
错误scanf(“%d”,&a)或scanf(“%ld”,a);
printf(“%d”,a)
正确scanf(“%ld”,&a)
Printf(“%ld”,a)
切记此处还会发生一个小错误,在输入变量时忘记添加地址符&,本人忘加地址符的此时有n多次,只要编译器一报错我立马就知道是犯了这样的错误。
在用scanf进行多个数据的输入时建议大家用这样的格式(以整型数据为例)
Scanf(“%d%d”,&a,&b)
在用键盘输入时数据之间只需用空格隔开即可,这与以后学习的c++是对应的,并且简单不易犯错。还有其他的输入方式。
现在将常见不同类型数据的输入输出格式列表如下
Int | %d |
Double | %lf |
Float | %f |
Char | %c/%s(字符型数组用%s) |
Long | %ld |
在这里说一下typedef、define吧,typedef可以创造新的数据类型,用以避免数据类型不足时产生的尴尬。就我现在所接触到的最多的就是这样的类型:
1):tupedef创建新类型
Typedef struct student
{
Int num;
Double score;
Char name;
}stu;
这定义了一个结构体类型,stu内包含了多个数据类型,在程序中若出现stu,就表示这样的一个数据类型;
2):宏定义
#define n 100;
宏定义的define是典型的“后替前”,即当程序中出现n时,就会用100代替,用在程序中可以做到“一改全改”的效果,避免了程序在某些情况下繁琐的修改过程,节省了时间,提高了效率。
还有一些尚未列出,在以后的学习中慢慢会遇到,大家自己体会。学习注重的是自悟!
因为在不同的编译器上数据的储存空间(分配的字节)不同,所以它的值的范围也就不同,超出范围就会溢出!
(2)
有关运算符的小陷阱
常见的运算符
初等 | () |
单目 | !a、++ —— |
算数 | + - * / % |
关系 | != > < <= >= == |
逻辑 | && || != |
条件 | 表达式1?表达式2:表达式3 |
赋值 | = |
逗号 | 表达式1,表达式2,……,表达式n |
例二:int a=3,b=4 求a/b
错误:a/b=0.75 正确:a/b=0 解析:这和我们以前的的数学运算不一样,因为a、b都是整形变量,取整后得0
还有一个运算符常用%(求余)此运算符常用在(1)将10进制数转化为二进制时(2)将一个数逆序输出。
现将十进制转二进制的代码写出如下,算是对求余和取整的复习吧!
#include<stdio.h>
int action(int m)
{
int a[30],n=0,i;
while(m)
{
a[n]=m%2;
m=m/2;
n++;
}
for(i=n-1;i>=0;i--)
printf("%d ",a[i]);
}
int main()
{
int a=10;
action(a);
getch();
return 0;
}
(3) 转义字符
转义字符有很多,像换行\n,退格\b…….用的多了,自然就明白。转义字符就我所接触到的,主要是关于其所占字节的问题(sizeof()),平时不是太常用,主要在考试题中出现,具体出题的方式就是给你一串字符,夹杂着一些转义字符,求这串字符所占字节数。建议遇到这样的题再考虑,有时候我也搞不清楚,建议去百度一下。不好意思了!
B:选择语句
(1)if else
判断程序在执行过程产生的数是否与目标数相同,相同就继续执行
例一:
错误:int a
Scanf(“%d”,&a);
if(a=3);
Printf(“%d”,a);
Else
Printf(“you are wong”);
正确:int a
Scanf(“%d”,&a);
if(a==3)
Printf(“%d”,a);
Else
Printf(“you are wong”);
(1) 这种情况出现很多,一看就明白,但是在做时往往会遗忘。在调试程序时,有可能就是因为这样的原因,程序达不到理想的结果。遇到这样的情况,要小心一点哈!
(2) If语句控制的范围是离它最近的一个分号前的内容,若要if语句控制多个语句,应用大括号括起来。如下
If()
{
语句1;
语句2;
……;
语句n;
}
if()后切记不要加分号,在调试程序时,有可能就是因为这样的原因,程序达不到理想的结果。我遇到了很多次,找错误时不容易发现。
(2)Switch case(多分支选择语句)
1:给一个年份判断该年份的某月有几天就可以用它。要合理运用break跳出。
2:在做小菜单时用它最爽了。给一个例子吧。
#include<stdio.h>
int meum()
{
printf(" 1:加法 \n");
printf(" 2:减法 \n");
printf(" 3:乘法 \n");
printf(" 4:除法 \n");
printf(" 5:退出操作 \n");
printf("你的选择【 】\b\b");
int n;
scanf("%d",&n);
return n ;
}
int main()
{
int m;
double x,y,z;
while(1)
{
m=meum();
switch(m)
{
case 1 :printf("请输入两个数\n");
scanf("%lf%lf",&x,&y);z=x+y;
printf("%lf=%lf+%lf",z,x,y);
break;
case 2 :printf("请输入两个数\n");
scanf("%lf%lf",&x,&y);
z=x-y;
printf("%lf=%lf-%lf",z,x,y);
break;
case 3 :printf("请输入两个数\n");
scanf("%lf%lf",&x,&y);
z=x*y;
printf("%lf=%lf*%lf",z,x,y);
break;
case 4 :printf("请输入两个数\n");
scanf("%lf%lf",&x,&y);
if(y==0)
{
printf("你输入有误\n");
break;
}
else
z=x/y;printf("%lf=%lf/%lf",z,x,y);break;
case 5:printf("你选择了退出,再见\n");
getch();return 0;
default :printf("你的输入不在可执行范围内,请重新输入");
}
printf("\n");
}
getch();
return 0;
}
对上述小菜单的解析:
(1) 它包含了菜单界面和值处理两个模块。菜单界面太简化,只是为了说明情况。
(2) 菜单处理界面调用了函数,返回值作为值处理的选择项。学回函数的调用以及返回值的利用对以后自己的发展非常有用。这点在以后的内容中还会再提到,这里只是让大家在心底有个意识。
(3) 在值处理的过程,用switch case做框架,每个选择都有不同的功能。兼用了循环的知识。还可以添加清屏函数,使界面清晰。添加颜色函数,使字体颜色多样化。
(4) 课程设计会用到小菜单的内容,希望你能懂!
C:循环
循环有三种形式(1)do{循环体}while(循环执行的条件);
(2)while(循环执行条件){循环体}
(3)for(循环变量赋初值;循环条件;循环条件增值)
{循环体}
对于前两种循环来说,区别在于是先执行循环体,再进行循环的判断,还是先进行循环的判断,再执行循环体。
下面就同一个问题求sum=1+2+3+……+99+100用这两种循环做例子,大家体会一下。
#include<stdio.h>
int main()
{
int sum=0,i=1;
while(i<=100)
{
sum=sum+i;
i++;
}
printf("sum=%d",sum);
getch();
return 0;
}
#include<stdio.h>
int main()
{
int sum=0,i=1;
do
{
sum=sum+i;
i++;
}while(i<=100) ;
printf("sum=%d",sum);
getch();
return 0;
}
对于for( 一;二 ;三 ){循环体}来说:两个分号将括号分为三个部分“一”、“二”、“三”,循环在执行过程时,先执行“一”,且“一”只执行一次,接着判断是否符合循环执行条件即执行“二”,若符合条件,执行循环体,执行完毕后,执行“三”,即循环变量增值,再次执行“二”,接着如上叙述执行,直到不符合循环执行条件为止。For循环最典型的例子是“九九乘法表”,现将该例子写出,以便思考。
#include<stdio.h>
int main()
{
int i,j,z;
for(i=1;i<=9;i++)
{
for(j=1;j<=i;j++)
{
z=i*j;
printf("%d*%d=%d\t",i,j,z);
}
printf("\n");
}
getch();//让时间暂停
return 0;
}
D:数组
(一):数组,顾名思义就是一组数,它是一组数的集合,数组的使用包括:1定义数组2数组的初始化 3数组的引用
1定义数组
类型符 数组名[常量表达式]
类型符:规定了数组的数据类型,如int ,char等(字符数组很奇妙,以后还要单独说)
数组名:必须符合表示符的命名规则
常量表达式:表示数组的长度(若是多维数组可以有多个常量表达式)
2数组的初始化
以初始化整形数组为例
int a[100]={1,2,3,4,5,6} 该数组包括a[0]~a[99]共100数据,系统规定数组的的下标从0开始,不存在a[100]。其中a[0]~a[5]分别为1、2、3、4、5、6,未赋值的系统自动赋值为0,即a[6]~a[99]都为0。
3数组的引用
引用方式:数组名[下标]
想要使用数组内的某个数据只要给出数组名和数组下标即可,对于数组a[100]来说,a[0]就代表数据1,a[5]就代表数据6;
以为一维数组排序为例,排序方法为选择排序,说明上述问题。
#include<stdio.h>
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1};//定义长度为10的数组,并初始化a[0]~a[8],a[9]
//自动赋0,
int i,j,t;
printf("原数组为:\n");
for(i=0;i<10;i++)
printf("%d\t",a[i]);
for(i=0;i<9;i++)
{
for(j=i+1;j<10;j++)
{
if(a[i]>a[j])
{
t=a[i];
a[i]=a[j];
a[j] =t;
}
}
}
printf("排序后数组为:\n");
for(i=0;i<10;i++)
printf("%d\t",a[i]);
getch();
return 0;
}
注:多维数组的用法与上述差异不是太大,就不在再次说了!呵呵!
(二):字符型数组
字符数组的定义,初始化,引用和上面的例子int型数组一样,但有一点区别。
1:字符数组是以“\0”作为结束标志 ,这是系统自动加的;
2: 定义字符数组时字符数组的长度要尽量预留一个位置存放“\0”,不然陷阱多多。总而言之,实践的多了,就会知道字符数组的陷阱了,实践出真知啊!
学会用字符串处理函数。
1:输入字符数组
gets(字符数组)
2:输出字符数组
Puts(字符数组)
3:字符串复制函数
Strcpy(字符数组1,字符数组2)
我认为以上三个字符串处理函数比较重要,经常要用尤其是字符串复制函数,在结构体中经常用,像输入某人的名字,判断符合程序的某种要求后把它存入结构体,以便再次使用,就会用到它,至于其他的字符串处理函数知道并会用就行。
最典型的例子是“字母小写变大写,大写变小写”。
#include<stdio.h>
int main()
{
int i;
char a[100];
gets(a);
for(i=0;a[i]!='\0';i++)
{
if(a[i]>='A'&&a[i]<='Z')
a[i]=a[i]+32;
else
if(a[i]>='a'&&a[i]<='z')
a[i]=a[i]-32;
}
puts(a);
getch();
return 0;
}
E函数
函数只要“有一个接口,一个出口”,就可以把复杂的程序连接起来。它为程序化大为小,实现模块化,避免了程序的繁长,为团队之间协作完成某一复杂的工程提供了便利的途径,高效快捷,既提高了团队协作能力,又锻炼了个人能力。所以说学会函数很重要。
函数的定义
1有返回值
Void 函数名(参数)
{
函数体
}
2无返回值
函数返回值类型名 函数名(参数)
{
函数体
Return (调用函数后的处理结果)
}
1:其实第二种包含第一种,这只是我的理解,第一种返回值类型为空而已,第二种用的更多,有代表性,就把这两个单独列出来了。
2:参数可以有,也可以为空。函数作为简化程序的一部分,要尽量做到有接口,有出口。
3:函数要尽量做到见名知意。当被调用函数过多时,可以快速知道该函数的作用,避免了思绪的混乱。
4:被调用函数尽量写在main()函数之前。若放在main()之后,需要事先在main()里做声名,有点麻烦,视个人习惯而定。
在函数的调用过程中,要注意的问题。
1):调用函数时函数名要对应。
想调用函数求两者中的最大值应调max(a,b),不要掉min(a,b),只是最低级的错误。
2):实参与形参的对应。
形参与实参的个数的要相同,并且形参和实参的类型要一致。在调用函数之后,在调用函数内,形参也可以作为一个已知数据使用。
3):返回值的对应
调用函数时,函数的返回值类型要和经过程序处理之后的数据类型一致。对于有返回值的函数,在main()函数中还要接收返回值,不能有去无回。(这里实际上还涉及到强制类型转化的知识)
4)关于函数的嵌套调用。
以求三个数最大值为例,可以这样用。
M=max(a,max(b,c))
这是一个函数被用了两次,嵌套三次甚至更多次也是如此。
5)函数的递归调用
以课本上求年龄为例。
#include<stdio.h>
int age(int n)
{
int c;
if(n==1)//第一个人的年龄为十岁
c=10;
else
c=age(n-1)+2;//第二个比第一个大两岁,以后依次类推。
return c;
}
int main()
{
printf("%d",age(5));//求第五个人的年龄
getch();
return 0;
}
6)函数与数组
a:可以把数组传入函数中进行处理,只不过返回值只能有一个。
b:要想返回数组,可以再main()中传一个,处理以后再返回一个结果即可。c:要想返回一组数,可以用链表。链表很强大。
d:学会数组的使用,传结构体等就会手到擒来。
这是一个传结构体的例子,了解就可以。
有五个学生,每个学生的数据包括学号,姓名,三门课的成绩,从键盘输入五个学生的数据,要求打印出每个学生的平均成绩,以及最高分。
要求:用一个函数输入五个学生的数据;用一个函数求总平均分;用函数找出最高分学生数据,总平均分和最高分的学生的数据都在主函数中输出。
#include<stdio.h>
struct student
{
int num;
char name[20];
double math;
double chinaese;
double pe;
double average;
}s[5];//定义结构体数组
void action(struct student s[],int n)//传入结构体数组,求最大值
{
int i,m;
double max=s[0].average;
for(i=1;i<n;i++)
{
if(s[i].average>s[0].average)
max=s[i].average;
m=i;
}
printf("max=%lf ",max) ;
}
double add(struct student s[],int n) //传入结构体数组,求平均分
{
int i;
double sum=0,av;
for(i=0;i<n;i++)
{ scanf("%d%s%lf%lf%lf",&s[i].num,s[i].name,&s[i].math,&s[i].chinaese,&s[i].pe);
s[i].average=(s[i].math+s[i].chinaese+s[i].pe)/3;
sum=sum+s[i].average;
}
av=sum/5;
action(s,5);
return av;
}
int main()
{
printf("average=%lf\n",add(s,5));
return 0;
}
在函数的学习过程中还会涉及到“全局变量”和“局部变量”,以及变量的生存期、存储方式。
全局变量:在宏定义“#define n 100”中的“n”就是一个全局变量。类似的就是全局变量。
从程序开始执行到程序的死亡,这之间全局变量一直在发挥着它的作用,它随程序生而生,随程序死而死。这也它的生存期,它的作用域是全部程序。全局变量全部放在静态存储区。
局部变量:它只在程序执行的某一阶段发挥作用,这时它活着,程序执行完这一段之后,它就死掉了。这也是它的生存期,局部变量在函数中出现的较多,这样的变量在函数中得到了完美的体现。它的存储类别有四种 auto(自动)、static(静态)、register(寄存器),extern(外部的)
变量的存储方式:
在内存中存储空间可分为,程序区,动态存储区和静态存储区三种。
动态存储区存放以下数据:
(1:函数形式参数。
(2:函数中定义的没用关键字static声明的变量。
(3:函数调用时现场保护和返回地址。
有这样的一个例子,分析一下吧!
#include<stdio.h>
#define n 1
int a=10;
int main()
{
int b=5,c=8,d=2,i;
c=b+d;
{
int b=3,c=6;
for(i=0;i<n;i++)
{
c=b+d;
b=a-1;
d=d-b;
}
printf("a=%d b=%d c=%d d=%d\n",a,b,c,d);
}
printf("a=%d b=%d c=%d d=%d\n",a,b,c,d);
getch();
return 0;
}
F指针
地址形象的称为指针。一个已经定义的变量,想去引用它,有两种方式。一个是通过变量名,一个是通过访问该变量所在的地址。这就好比一栋房子的每个房间一样,你可通过门牌号找到这个房间的,对房间的东西进行操作,这个门牌号就是计算机给内存分配的地址,这是一种间接地方式。另外的方式就是直接找到这个房子的东西进行操作,这是一种直接的方式。
定义指针变量
类型名 *指针变量名
例:int *a
想引用一个指针变量 ,可以采用这样的方式
Int a=10;
Int *b=&a(int *b;b=&a)
Printf(“%d”,*b);
一般就是先定义一个变量a,再定义一个同类型的指针变量b,给指针变量b一个指向b=*a即可。
下面的这个例子通过引用指针变量求最大值
#include<stdio.h>
int main()
{
int a=10,b=5,*c,*d;
c=&a;//给指针一个指向
d=&b;
if(*c>*d)
printf("%d",*c);//输出该指针指向的内容
else
printf("%d",*d);
getch();
return 0;
}
指针其实给了我们很大的方便,给了我们随时随地取用变量的方法,就是找到存放变量的地址。
指针可以作为函数的参数,返回值一个是指针,一个是普通变量,对于字符型数组也是如此。
int *max(int *a,int *b)
{
if(*a>*b)
return a;
else
return b;
}
int main()
{
int a=10,b=5,*c,*d;
c=&a;
d=&b;
c=max(c,d);
printf("%d",*c);
getch();
return 0;
}
#include<stdio.h>
int max(int *a,int *b)
{
if(*a>*b)
return *a;
else
return *b;
}
int main()
{
int a=10,b=5,*c,*d;
c=&a;
d=&b;
a=max(c,d);
printf("%d",a);
getch();
return 0;
}
在下面的例子中还涉及到指针数组和数组指针的区别。指针数组一般是指向多维数组的。
指针数组:类型名 * 数组名 [数组长度],Int * p[4],[]的优先级比*高,所以这是一个指针数组,指向多维数组。
数组指针:类型名 *数组名 [数组长度],例int * p[4]
指针也可以多重的,这主要和多维数组有联系,这需要理解指针的跨步,即一步平移多少个字节。以二维数组为例,说明这个问题,这也是通过指针引用数组的例子。
#include<stdio.h>
int main()
{
int a[2][3]={1,2,3,4,5,6};
int *b,i,j;
b=*a;//指针指向数组起始地址
//多重指针每次移动是跨行的,
for(i=0;i<2;i++)
printf("%d\t",*(*(a+i)));
//要想输出数组的每个数据要采用以下的方法
printf("\n");
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
printf("%d\t",*(*(a+i)+j));
}
}
getch();
return 0;
}
通过指针引用字符串。
#include<stdio.h>
int main()
{
int i;
char a[]="qwertyuiop";
char *b;
b=a;
for(i=0;a[i]!='\0';i++,b++)
printf("%c",*b);
getch();
return 0;
}
G:动态内存分配和指向它的指针变量
以前就介绍过,全局变量是分配在内存中的动态储存区的,非静态的局部变量是分配在内存中的动态储存区的,这个储存区是一个称为“栈”的区域。C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据在需要时开辟,不需要时随时释放。这些数据是临时存放在一个特别的自由储存区的,称为“堆”区,可以根据需要,向系统申请所需空间大小,这些只能通过指针来引用。
内存的动态分配主要需要以下几个系统提供的库函数,分别是malloc,calloc,realloc,free函数。
需要的头文件是#include<malloc.h>或#include<stdlib.h>
(1):malloc(申请空间),calloc(申请的空间个数,每个空间的字节数)
使用模式:(类型名 *)malloc(字节数)
例 Int *q = (Int *)malloc(sizeof(int))//在此编译系统中申请一个int类型数据所需要的空间,想对一个数组申请空间可以用calloc函数,
若申请50个4字节空间,可以用calloc(50,4)
如果申请不成功,返回一个空指针NULL
如果申请的空间不够用或者太大了,可以用下面的函数改写申请的空间。
(2)Realloc(原来申请的动态空间名字,大小)
例:对于用malloc申请的空间,可以这样改写
Realloc(q,50)
此时它已被改写为50字节空间,而不是一个int数据所需的空间,如果申请不成功,realloc返回一个空指针NULL
(3) free(空间名)
对于申请的空间,若以后不再需要它,可以释放空间,就可以用它了
使用模式:free(q)
free函数无返回值
以上的内容对于用链表处理大批量数据是非常有用的,是经常要用到的,所以别看他简单,其实吧内涵丰富,还需要仔细体会,感悟。
对于指针这一章节来说,它太灵活了,正是因为它的灵活,它才为c语言注入了生机,要想用一句话两句话把它说清,我认为很难,我发现越是到难得地方我就不知道如何用语言表达我的想法了,所以我觉的最重要的是大家在实践中体会。
H:用户建立自己的数据类型
(一) 结构体
定义结构体
Struct 结构体名
{
类型 数据1
类型 数据2
……
类型 数据N
}结构体变量名;
可以把结构体看做一个特殊的int类型的数据,只不过它里面又包含了多个类型的数据,结构体的初始化,引用如下所示。
方式一:#include<stdio.h>
struct student
{
int num;
char name[10];
double math;
};
int main()
{
struct student a;
char name[20]="qwert";//初始化姓名
a.num=1;
strcpy(a.name,name);/*用复制函数将姓名复制进入结构体,在初学时易a.name=”qwert”的错误*/
a.math=80.5;
printf("num=%d name=%s math=%lf",a.num,a.name,a.math);
getch();
return 0;
}
方式二:
#include<stdio.h>
struct student
{
int num;
char name[10];
double math;
}a={1,"qwert",80.5};
int main()
{
printf("num=%d name=%s math=%lf",a.num,a.name,a.math);
getch();
return 0;
}
方式三:
#include<stdio.h>
struct student
{
int num;
char name[10];
double math;
};
int main()
{
struct student a,*b;
char name[20]="qwert";
a.num=1;
strcpy(a.name,name);
a.math=80.5;
b=&a;
printf("num=%d name=%s math=%lf",b->num,b->name,b->math);
getch();
return 0;
}
解析:1):方式一和方式二差别不是太大,一看就懂了,方式三是结构体和指针的结合使用。
2):要学会”.”运算符和在引用含有指针变量的结构体时用“->”运算符。
3):此外还要知道结构体所占字节数;上面例子中的结构体所占字节是24,它是基本数据类型的整数倍。在此例子中double类型数据为基本类型,4+10+8=22,它是基本类型的整数倍,所以结果是24。
4):知道了结构体如何定义、引用就会知道公用体和枚举的定义引用方法,因为它们是相同的。
(二) 共用体(联合体)
共用体的定义,引用方式和结构体相同。
共用体的特殊之处
1):所谓共用体,就是申请一块内存,一起共享这块内存。
2):这块内存的大小是共用体中最大类型的数据所占的内存。
#include<stdio.h>
union student
{
int num;
char name[10];
double math;
};
int main()
{
union student a,*b;
char name[20]="qwert";
a.num=1;
strcpy(a.name,name);
a.math=80.5;
b=&a;
printf("num=%d name=%s math=%lf\n",b->num,b->name,b->math);
printf("%d ",sizeof(union student));
getch();
return 0;
}
在该例子中试图给该公用体所有的变量赋值,但结果以最后一次赋值的结果为最终结果,这就是共用体的妙处。它是以最后一次赋值的结果为最终结果,因为它们公用一块内存,当该内存再次写入以前写入的就被覆盖了。
它所占内存的大小是16字节,以为char name[10]为10个字节,但它是基本数据类型double的整数倍,所以是16字节
(三) 枚举
枚举的定义,引用方式和结构体相同。
枚举的特殊之处
1):编译系统把枚举元素当做常量处理,从零开始,如果对某个枚举元素赋值,该枚举元素就为所赋的值,它以后的枚举元素就在它的基础上依次增加。
2)它可以用做判断比较。
(四) 链表
所谓链表,就是一个数据链,它能把数据穿在一起,可以做到抓住头,找到尾,能把所有数据过滤一遍,它非常重要。
在学习链表的过程中,要结合例子,理解链表。理解某些主要程序语句的意思,这些主要程序语句是把链表穿起来的关键,其他的能看懂就可以,因为这些主要程序语句的逻辑新很强,理解起来很有难度,我在学习链表的过程中,曾把例子抄写下来,一遍又一遍,每一遍的抄写都是我对链表的理解更深一层。把这个链表模板给出以便学习思考。
蓝色部分为主要程序语句,它是和动态内存分配结合在一起的,用到结构体,指针,循环,等的知识。
#include <stdio.h>
#include <malloc.h>
//保留数据的节点
typedef struct node
{
int data;
struct node *next;
}Node;
//链表的管理节点,包括表头和链表节点的数量
typedef struct nodectrl
{
Node *head;
int num;
}NodeCtrl;
//申请管理节点的空间,并初始化表头和节点数量值
NodeCtrl *CreateCtrl()
{
NodeCtrl *nc = (NodeCtrl*)malloc(sizeof(NodeCtrl));
if(nc == NULL)
{
puts("Create NodeCtrl failed!");
return NULL;
}
//由于刚建立管理节点,链表中没有任何节点
nc->head = NULL;
nc->num = 0;
return nc;
}
//根据管理节点内存位置,加入一个新的数据(数据节点)
int AddData(NodeCtrl *nc,int data)
{
Node *p,*q;
if(nc == NULL)
return -1;
//新申请一个Node节点,用来保留data数据
q = (Node*)malloc(sizeof(Node));
if(q == NULL)
return -1;
//保留数据
q->data = data;
q->next = NULL;
//找到链表的头节点,准备插入数据
p = nc->head;
//插入数据(头插法)
if(p != NULL)
q->next = nc->head;
nc->head = q;
//由于增加了一个节点,所以节点数量需要加1
nc->num++;
return 0;
}
//根据管理节点,显示表头所在链表中的所有存储值
void Display(NodeCtrl *nc)
{
Node *p = NULL;
if(nc == NULL)
return;
printf("Current list has %d data!\n",nc->num);
//num记录的是节点数量,如果为0,表示是空表,否则有数据
if(nc->num > 0)
{
//找到表头
p = nc->head;
//如果节点不空
while(p != NULL)
{
printf("%d\n",p->data);
p = p->next;
}
}
}
//释放管理节点保留的链表,先释放链表,在释放管理节点
void FreeNodeCtrl(NodeCtrl *nc)
{
//定义两个临时指针,q负责保留上次的节点,p负责向下寻找下一个节点
Node *p,*q;
if(nc == NULL)
return;
//如果num大于0,表示链表中有大于等于1个节点,所以必须释放
if(nc->num > 0)
{
//找到管理节点保存的链表表头
p = nc->head;
//如果节点不空
while(p != NULL)
{
//先保留p节点的地址
q = p;
//p节点向下移动,此时q在p上一个节点位置
p = p->next;
//释放p节点的上一个节点
free(q);
}
}
//链表节点释放完毕,将管理节点的数据清0
nc->head = NULL;
nc->num = 0;
//释放管理节点
free(nc);
}
int main()
{
int i,data;
NodeCtrl *nc1 = CreateCtrl();
NodeCtrl *nc2 = CreateCtrl();
for(i=0;i<3;i++)
{
scanf("%d",&data);
AddData(nc1,data);
}
for(i=0;i<20;i++)
AddData(nc2,i);
Display(nc1);
FreeNodeCtrl(nc1);
Display(nc2);
FreeNodeCtrl(nc2);
return 0;
}
(五) 宏定义和typedef类型
typedef可以创造新的数据类型,用以避免数据类型不足时产生的尴尬。就我现在所接触到的最多的就是这样的类型:
1):Typedef struct student
{
Int num;
Double score;
Char name;
}stu;
这定义了一个结构体类型,stu内包含了多个数据类型,在程序中若出现stu,就表示这样的一个数据类型;
#include<stdio.h>
typedef int (*fp) (int x,int y);
int call(fp p,int x,int y)
{
return p(x,y);
}
int add(int x,int y)
{
return x+y;
}
int main()
{
fp p;
printf("%d",call(add,2,3));
getch();
return 0;
}
此例子用到typedef创建新类型,和函数回调以及指针的知识
2):#define n 100;
宏定义的define是典型的“后替前”,即当程序中出现n时,就会用100代替,用在程序中可以做到“一改全改”的效果,避免了程序在某些情况下繁琐的修改过程,节省了时间,提高了效率。
简单的代换
#include<stdio.h>
#define MYINT int x,
int main()
{//当遇到MYINT时,int x,就代替了MYINT
MYINT a,b;//后代替前
x=2;
printf("x=%d\n",++x);
getch();
return 0;
}
通过下面这个例子大家可以理解代替的深层含义
#include<stdio.h>
#define swap(x,y) x*y
int main()
{
int a=3,b=7;
printf("a=%d\na=%d",swap(a,b),swap(a+1,b));
getch();
return 0;
}
I文件
程序在执行完各种操作之后,需要将程序运行的结果保存以便随时取用,这就需要将程序运行结果保存入文件。
保存文件前需要知道
(一)文件的打开(创建)方式
头文件 #include<stdlib.h>
定义 文件指针 FILE *fp
fp=fopen(“文件名”,”读写方式”)
如果创建文件失败,返回一个空指针NULL
一般用这样的方式创建文件
FILE *fp
If((fp=fopen(“file.Txt”,“r”))= =NULL);
{
Printf(“cannot open file”);
exit(0);//强制程序关闭
}
文件使用方式 | 含义 | 如果指定的文件不存在 |
r(只读) | 为了输入数据,打开一个已存在的文本文件 | 出错 |
rb(只读) | 为了输入数据,打开一个二进制文本文件 | 出错 |
W(只写) | 为了输出数据,打开一个已存在的文本文件 |
建立新文件 |
Wb(只写)
|
为了输出数据,打开一个二进制文件
| 建立新文件
|
r+(读写) | 为了读和写,打开一个文本文件 | 出错 |
W+(读写) | 为了读和写,建立一个新的文本文件 | 建立新文件 |
a(追加 | 向文本文件尾添加数据 | 出错 |
a+(读写) | 为了读和写,打开一个文本文件 | 出错 |
Wb+(读写) | 为了读和写,建立一个新的二进制文件 | 建立新文件 |
ab+(读写) | 为读写打开一个二进制文件 | 出错 |
还有其他的读写方式,这是常用的读写方式
(二)将数据写入文件
1:用格式化的方式读写文件(这种方式不常用,知道就可以了)
fprintf(文件指针,格式字符串,输出表列)
fscanf(文件指针,格式字符串,输出表列)
2:以二进制方式向文件读写一组数据
fread(地址,大小,次数,fp)
fwrite(地址,大小,次数,fp)
还有其他的方式,只不过那些功能不是太强大,就没有列出来
(三)文件的关闭方式
在文件读写完毕之后,就可以使用它关闭文件,通常fopen和fclose成对出现
fp为定义的文件类型指针
fclose(fp);