数据类型
变量
变量三要素:
- 名称(标识符)
- 变量位置(&变量名)
- 变量的值
变量用",“表示分隔,用”;"表示结束
int
----整型----格式说明----%d
十进制整型,%o
八进制整型,%x
十六进制整型
float
----实型----格式说明----%f
实型
char
---- 字符型----格式说明----%c
字符型,%s
字符串
%4d
输出结果占4个空位,右对齐,不够补空格
%-4d
输出结果占4个空位,左对齐,不够补空格
#include<stdio.h>
void main() //主函数
{
}
整型常量
八进制数:以0开头(010,012)
十六进制数:以0x开头(0x10,0x12,0x22)
实型常量
- 小数形式,如 3.14,-9.8
- 指数形式,如
1.2x10^5==1.2E(e)+5
3.14x10^9==3.14E+9
9.8x10^3==9.8e+3
%e
—指数形式输出,%f
—小数形式输出,%g
—选择%f
与%e
只输出列最小的输出,并去掉无意义的0。
转义字符
\n
----换行\t
----横向跳格\b
----退格\r
----回车\'或\"
----输出单引号或双引号\\
----输出斜杠
注意: ‘a’,‘b’-----注单引号中只能有一个字符,‘a’,‘ab’
ASCLL码
a---->97,A---->65,0---->48,空格---->32
符号常量
#include<stdio.h>
# define pi 3.14
void main()
{
…………
s=pi+a+b //将pi自动替换为3.14
…………
}
运算符
优先级
逻辑非(!) > 乘除,取余 > 加减 > 大于,小于,大于或等于,小于或等于 > 等于,不等于 > 逻辑与(&&) > 逻辑或(||) > 赋值运算符(=)
C语言中的除运算(/)
是整除,例如:5/2=2(舍去小数部分)
;%(取余)
:要求两边的操作数均为整数。
算术运算符(加减乘除等)>赋值运算符(用"=“表示,”="右边的值赋值给左边)
a=12,a+=,a-=,a * a,求a=?
解:
a * a=12x12=144,a=a-144=-132,a=a+(-132)=-132-132=-264
.7== 0.7 , 12.== 12.0 , .8==0.8
自加与自减
自加: int i=10;
j=++i;(先让i自增,再参与运算)
j=i++;(i先参与运算,再自增)
自减: int i=10;
j=--i;(先让i自减,再参与运算)
j=i--;(i先参与运算,再自减)
关系运算符
等号(==),不等号(!=)
C语言中用整数"1"表示逻辑真,用整数"0"表示逻辑假
与(&&), 或(||), 非(!)
C语言中, 0,0.0,'\0',NULL
都为假,其余均为真
"a" && 2
该表达式为真
逻辑与优化
int a=0, b=0;
int i=a && ++b;
//结果:i=0,a=0,b=0(++b被优化了,不作运算)
逻辑或优化
int a=1, b=0;
int i=a || ++b;
//结果:i=1,a=1,b=0(++b被优化了,不作运算)
条件语句
switch
switch(表达式)
{
case 常量表达式1:语句序列; break; //执行的语句如printf
case 常量表达式2:语句序列; break;
case 常量表达式3:语句序列; break;
case 常量表达式4:语句序列; break;
case 常量表达式5:语句序列; break;
default : 语句序列; break;
}
//表达式的值满足哪个常量表达式就执行哪个表达式后的语句,
//都不满足就执行默认后的语句
if—else
if(表达式1)
{
执行语句1;
}
else if(表达式2)
{
执行语句2;
}
else
{
执行语句3;
}
//表达式1为真时执行语句1;表达式2为真且表达式1为假时执行语句2;
//否则执行语句3
循环
while循环
while(表达式)
执行语句;
//当表达式为真时,就一直循环执行语句
do–while循环
do
{
执行语句;
}
while(表达式);
//当表达式为真时,就一直循环执行语句,表达式为假时,
//则只会执行一次
for 循环
for(表达式1;表达式2;表达式3)
{
执行语句;
}
函数
库函数
val=getchar()
输入一个字符将其存入变量val中(无参函数)
gets(val)
输入字符串,将其存入val变量中
putchar('a')
输出一个字符’a’(有参函数)
puts('abc')
输出字符串’abc’(有参函数)
pow(x,y)
获得x的y次方的值
用户自定义函数
函数类型标识符—int,float,char等,表示返回值的类型
return
将值返回给主调函数
函数类型标识符 函数名(参数类型 参数名,……) //函数头
{
执行语句 //函数体
}
函数调用
主调函数
主调函数:主动去调用别的函数(其中参数叫实参)
函数类型标识符 变量名=函数名(参数1,参数2); //主调函数
例如:int val=Add(2,4);
被调函数
被调函数:被调用的函数本身(其中参数叫形参)
形参的改变,对应的实参不受影响,实参--单向值传递--->形参
注意:若主调函数在被调函数之前,则需要提前声明(即在主调函数之前,将定义的函数头,写出来)
#include<stdio.h>
int Add(int a,int b); //函数声明
int main()
{
int a=2,b=4,c=0;
c=Add(a,b); //调用函数(主调函数)
printf("结果为:%d",c);
return 0;
}
int Add(int a,int b) //函数定义(被调函数)
{
return a+b;
}
数组
数组:将相同类型的有限个数据排列在一起的集合。
一维数组
数组元素类型 数组名[数组大小(此处必须是个常量)
]={数据1,数据2,数据3……}
int Array[4]={120,340,65,97};
//调用数组中的某个元素
Array[下标]; //数组的下标从0开始
//Array[2] 表示Array数组第三个元素,即65
数组中未定义的元素默认为0;
在用户自定义函数中,若将数组名作为实参,对应的形参是数组形式(形参名[ ]),则形参变换,实参要跟着变换,
实参--双向地址传递--->形参
二维数组
类型说明符 数组名[常量表达式1][常量表达式2];
float array[4][2]; //共4行2列,有4x2个元素
int array[2][3]={{1,2,3},{4,5,6}};
={1,2,3,4,5,6};
//元素全部赋值时,数组第一维长度可以省略,第二维长度不能省略
// array[][3]; 这种写法可以用于形参与数组数组初始化,
//但不能用于定义
字符串
char 数组名[整型常量表达式]; //char array[20];
字符串初始化
- 对元素逐个赋值:
char array[5]={'c','h','i','n','a'};
- 字符串赋值:
char array[6]="china";
(注意:这里数组长度要比可见字符多一个,这个多的长度是用来存放'\0'
,'\0'
表示字符串的结束,自动添加) - C 语言中一个中文字符占2个字节:
char array[]="张三" (数组长度为5)
scanf函数可以直接使用字符数组(字符串)s,不需要在前面添加&符号
scanf("%s",s);<----->scanf("%s",&s[0]);
char s[]="I love\0 china";
printf("%s",s);
//打印的结果为"I love",原因:输出字符时遇见了'\0',提前终止了
gets(s); //输入字符串到s中,且自动在输入末端加上'\0',
//等价为scanf("%s",s);
puts(s); //输出字符串s,且自动在末端加上'\n'
字符串相关函数
头文件:#include<string.h>
字符串不能通过"="来赋值
strcpy(目标字符地址,源字符地址);
strncpy(目标字符地址,源字符地址,要复制的字符个数);
//strcpy():将源字符串包括结束符'\0'一同复制到目标字符所在地址的
//内存里
//strncpy():作用同上,但是是将源字符的前n个字符复制到目标字符所在
//地址的内存中,且不会复制结束符'\0',因此使用此函数后面最好加上
//'\0',否则可能出现乱码
strcat(目标字符地址,源字符地址);
//strcat()将源字符串连接到目标字符串后面,自动删除原目标字符串
//的'\0',将源字符串连同结束符'\0'一起放到目标字符串后面
strcmp(字符串1地址,字符串2地址);
//strcmp():将两个字符串中对应的字符的ASCLL码依次对比,
//直到不相等或比较到其中一个字符串的末尾为止
//若字符串1>字符串2,则函数结果为1
//若字符串1=字符串2,则函数结果为0
//若字符串1<字符串2,则函数结果为-1
字符的其他等价表示
char c;
c='a';
=97 //'a'的ASCLL码为97
='\141' //八进制数141==十进制数97
='\x61' //十六进制数61==十进制数97
//'\ddd':ddd处是1~3位八进制数所代表的字符
//'\xhh':hh处是1~2位十六进制数所代表的字符
指针基础
指针变量:专门存放变量地址的变量
(类比整型变量:专门存放整型数据的变量)
类型说明符 * 指针变量名
int *pt //表示指向一个整型数据的变量
//该变量名为pt
int n=99,*p; p=&n;//指针p指向了变量n的地址
//等价为int *p=&n;
//这里的*p就是指针
printf("%d\n",*p);
printf("%d\n",n);
//两者等价
*p=20; //将*p指针指向的地址所对应的变量赋值为20
char *a[10]; //存放了10个字符指针(指针数组)
C语言中,指针的大小都是4个字节
在数组中,两个指针相减(相加),所得结果是两个指针所指数组元素下标之差(指针一定要赋值后才能使用)
多级指针:
int *q,**p,i=99;
q=&i;
p=&q;
//*q指向变量i的地址,**p指向指针*q本身所在的地址
指针作为形参的函数调用时,是双向地址传递即形参影响实参
*p==p[0],**p==p[0][0];
指针与数组:
for(i=0;i<6;i++)
{
printf("%d\n",*(p+i));
printf("%d\n",p[i]);
printf("%d\n",*p++);
//三者结果是相同的
}
char *ch; //指针初始时指向数组的第一个元素
ch="hello!"; //字符串ch
char *s[]={"Cprogram","BASIC",
"computer English","Word"};
//字符串指针数组*s
char **p;
for(p=s,p<s+4,p++)
{
printf("%s\n",*p);
}
//*(*p+1)=='p'
//**(p+1)=='B'
//上面的两句注释一定要弄清楚两者区别
指针与二维数组
int s[3][4]={{0,2,4,6},{1,3,5,7},{9,6,2,1}};
int (*p)[4],i,j; //这里的*p只能指向每
//一行只有4个元素的数组
p=s;
scanf("%d%d",&i,&j);
printf("s[%d][%d]=%d\n",i,j,*(*(p+i)+j));
//输入1,2 输出s[1][2]=5
//*(p+i)==s[i]
//*(*(p+i)+j)==*(s[i]+j)==s[i][j]
a[2][3]=*(*(a+2)+3) //只有一级指针才能存放元素地址
p++=30(看为(p++))
++p=21(看为++§)
函数的调用
函数的嵌套调用
嵌套调用:在执行一个函数的过程中又去调用另一个函数,调用完后会返回第一个函数中继续执行之前未执行的内容
函数递归调用
直接递归:在执行一个函数的过程中,又去调用这个函数本身一次
间接递归:在执行一个函数的过程中,又去调用另一个函数,在执行另一个函数时,又去调用第一个函数,调用完后会返回上一个函数中继续执行之前未执行的内容,依次向上类推
#include<stdio.h>
long fac(int n)
{
if(n==1||n==0)
{
return 1;
}
else
{
t=n*fac(n-1);
return t;
}
}
void main()
{
int res=0,n=0;
scanf("%d",&n);
res=fas(n);
printf("结果是%d\n",res);
}
上述程序流程图:
fac(n)
函数调用了4次,递归了3次
递归调用:调用完函数后要返回主函数位置,继续执行未执行的内容
编译预处理与条件编译
编译预处理
引入文件或者函数库:
#include<文件名> //标准库函数头文件
#include"文件名" //用户自定义头文件
条件编译
#ifndef 头文件名
#define 头文件名
………………
#endif
//此处的头文件名必须全大写,
//前后必须添加下划线,'.'也变为下划线
//例如 stdio.h 变为 _STDIO_H_
宏定义
形式:#define 宏名 宏体
//无参宏
#define M (5*x+x*x)
void main()
{
int s,x;
scanf("%d",&x);
s=4*M+5*M+6*M;
printf("s=%d\n",s);
//当x=5时,s=750
}
//有参宏
#define M(y) 4*y+y*y
void main()
{
int s,x=5;
s=M(x);
printf("s=%d\n",s);
//当x=5时,s=45
//M(x)被替换为4*x+x*x即(4x5+5x5)
}
变量存储类型
局部变量
局部变量:其又被称为内部变量,在大括号内被定义,则定义的值只在该大括号内有效
全局变量
全局变量:在大括号外被定义,从被定义开始到程序结束,均有效
静态变量 static
静态变量static:static 类型变量只创建一次,只赋初值一次,若static变量未赋值,则默认为0(无论static使用多少次,其都使用上次的值,而不是定义时的初始值)
static 数据类型 变量名
寄存器变量register
寄存器变量register: 当要频繁的使用某个变量时,可以将其定义为寄存器变量,以提高程序运行速度
register 数据类型 变量名
外部变量extern
外部变量extern:拓展变量作用域(有效范围),从extern定义变量处开始
extern 数据类型 变量名
结构体、共用体与枚举
结构体
结构体是一种自定义的数据类型,它可以由其他不同的数据类型共同来构成一个全新的数据类型
struct 结构体名称(可省略)
{
数据类型 变量名;
数据类型 变量名;
…………………………
};
结构体中数据类型可相同,也可不同,不同结构体中的变量名可以相同,结构体定义在函数外面时,可以供其作用域内所有函数使用,结构体不会进行内存分配,只有在用结构体类型定义变量时,才会进行内存分配
先定义结构体类型,再定义结构体变量
struct student //定义结构体
{
char mum[10];
char name[10];
int chinese;
int match;
};
该结构体在内存中存放的位置如图:
struct student stu,s[10];
//定义结构体变量stu和数组s
typedef struct student AB
//将结构体类型赋与一个全新的名字AB
AB stu,s[10];
//与上面定义等价
//变量stu的所占字节为结构体内所有成员
//所占字节之和(即10+10+4+4=28)
//结构体类型的变量可以在定义结构体类型
//时同时定义
struct student //定义结构体
{
char mum[10];
char name[10];
int chinese;
int match;
}stu,s[10];
//若之前已经省略了结构体名,则在后面程序
//中将不能再定义新的该结构体的变量
结构体成员的引用:
结构体变量名.成员名称(普通变量名加".")
结构体类型指针->成员名称(指针用"->")
结构体类型变量 sum,*p
p=∑
(*p).成员名==p->成员名(两者等价)
//通过*p访问到sum,再用"."
结构体类型变量作为形参是单向值传递,结构体变量为指针时,是双向地址传递
结构体变量初始化:
变量名={数据1,数据2……} //{}内的数据按顺序依次赋值给成员,数据不全,则其余默认为0
struct student s[100];
strcpy(s[0].num,"20200001");
//给下标为0的结构体的成员num赋值
s[1].chinese=80;
//余下的成员变量赋值同理
(s+99)->match=90;
//等价于s[99].match=90;
共用体
union sample
{
char ch;
short s;
float f;
} un;
该共用体在内存中存放的位置如图:
共用体成员共享内存空间,该类型变量内存所占字节数由占内存最多的成员决定,而且后面写入的数据会将前面写入的数据覆盖掉,即数据只有最后一次写入才是完整的
枚举
enum 枚举名
{
枚举元素1,枚举元素2,……
//注意,各元素之间用逗号隔开
};
//注意,末尾有分号;
枚举在C语言中其实是一些符号常量集,直白点说:枚举定义了一些符号,这些符号的本质就是int类型的常量,每个符号和一个常量绑定,常量默认从0开始依次递增
与宏定义类似,但在定义多个的时候更方便
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
//上述代码等价于:
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
总结:宏定义先出现,用来解决符号常量的问题;后来人们发现有时候定义的符号常量彼此之间有关联(多选一的关系),用宏定义来做虽然可以但是不贴切,于是乎发明了枚举来解决这种情况
文件相关
文件类型与区别
二进制文件:以二进制数存放在计算机内,可以保存一切数据,如图片,音乐,电影等
文本文件:将文件转成ASCLL码存储,只能保存"文字"类型的数据,如小说,代码,文章
文件的打开与关闭
C语言提供一个已经定义好的结构体FILE,该结构体用于表示文件类型,表明对应变量是一个文件
文件名:
- C:\音乐\鸿雁.mp3 绝对路径
- 班级成绩表 相对路径,比绝对更快,但要保证文件在当前
{
…………………………
FILE *指针变量名
//所有文件的操作均离不开这个定义
//的FILE结构体类型的指针
}
#include<stdio.h>
FILE *fp \\定义FILE类型的指针fp
fp=fopen("文件名",操作)
//fopen()打开文件的函数
fclose(fg); //关闭文件
操作:
- “r” 打开已有文本文件,只允许读数据,若文件不存在,则打开失败
- "w"打开或创建文本文件,只允许写数据,若文件存在,则删除原文件数据再打开
- "a"打开已有文件,并在文件末尾追写数据
- “w+”,“r+”,"a+"均在原来的基础上添加对文本文件的可读可写
- “wb”,“rb”,“ab”,“wb+”,“ab+”,"rb+"作用对象为二进制文件,效果与文本文件类似
文件的读写
格式化读写函数
格式化读写函数 fscanf(),fprintf()
注意:读操作指从文件中读取数据到内存中,写操作指将内存中的数据写入文件
写函数:fprintf(指向文件的指针,格式化输出,要写入的数据所在地址)
读函数:fscanf(指向文件的指针,格式化输出,要读出的数据所存的变量)
#include<stdio.h>
typedef struct student
{
char No[20];
char Name[20];
int gradeC;
int grademath;
}stu;
int main()
{
FILE *fp;
stu A={"20200001","xxx",85,80},B;
if((fp=fopen("C:\\student.txt","w"))
==NULL)
{
return -1;
//文件打开失败,退出程序
}
fprintf(fp,"%s %s %d %d\n",
A.No,A.Name,A.gradeC,A.grademath)
fclose(fp); //关闭文件
if((fp=fopen("C:\\student.txt","r"))
==NULL) //绝对路径
{
return -1;
}
fscanf(fp,"%s %s %d %d\n",
B.No,B.Name,B.gradeC,B.grademath);
//将fg所指文件中的内容读出,并赋给结构体B
fclose(fp); //关闭文件
return 0;
}
若要数据写入文件后立即读出数据,则将"w",“r"换为"w+”,只用一个if从句就可以达到效果
字符读写函数
字符读写函数 fgetc(),fputc()
fgetc():从指定文件中读出一个字符,读出成功则返回读取到的字符,读取失败则返回EOF(-1)
fputc(): 从指定位置写入一个字符到文件中,写入成功返回写入的字符,写入失败,则返回EOF(-1)
ch=fgetc(FILE *fp);
//从文件指针fp中读取一个字符数据到变量ch中
fputc(int ch,FILE *fp);
//将变量ch的值写入指针fp所指文件
字符串读写函数
字符串读写函数 fgets(),fputs()
fgets(char *ch,int max,FILE *fp);
//从文件指针fp所指文件中读出最多max-1个
//数据放入*ch所指地址中,剩下一个
//存储结束符0(系统自动添加)
fputs(char *st,FILE *fp);
//将*st所指的字符串(st即首地址)写入目标文件
//中,写入成功返回0,不成功,则返回非0
feof(FILE *fp);
//若文件未结束,feof(fp)==假,否则feof(fp)
//为真
二进制数据块读写函数
二进制数据块读写函数 fread(),fwrite()
fwrite(stu,sizeof(stu),3,fp);
//stu 从该位置(一般是数组)读出数据
//sizeof(stu) 每次写入的数据的字节长度
//3 一次性写几个数据块
//fp 写入的目标文件
fread(&stu[0],sizeof(stu),1,fp)
//stu 将数据写入到的内存首地址
//sizeof(stu) 每次读出的数据的字节长度
//3 一次性读几个数据块
//fp 数据读取的文件
文件位置下标
rewind(fp);
//位置回到文件下标起点,即fpos=0
fseek(fp,0,SEEK_END);
//在*fp所指文件中将fpos指向相对于文件结尾偏移0字节的位置
//若偏移量为负,则是将fpos向前移动
//SEEK_END==2 fpos指向文件结尾
//SEEK_CUR==1 fpos当前所在位置
//SEEK_SET==0 fpos指向文件开头
无论是读取文件数据还是写入文件数据,文件位置下标fpos 都要自动向后移动到新的位置,以便读写操作,这跟人在纸上写字是一个道理
动态分配函数 malloc
引入头文件<malloc.h>
malloc()
函数作用:在堆上分配一片独立空间
(int*)malloc(N*sizeof(int))
//此函数会自动将其空间的地址返回
//N*sizeof(int) 为要分配的空间大小
free(a); //释放空间a
//realloc()函数:在堆上重新分配空间
(int*)realloc(p,N*sizeof(int))
//p 指向原来的空间所在的地址
//N*sizeof(int) 扩展的空间大小
//calloc函数:在堆上存放数组
(int*)calloc(5,sizeof(int))
// 5 要存储的数组长度
// sizeof(int) 数组每个元素的大小(字长)