第一章 C语言
1、基础知识
- 读C程序都是从main()(有且只有一个main函数)开始,再从上顺序往下读,碰到循环做循环,碰到选择做选择。
- 计算机的数据在电脑中以二进制的形式保存。
- bit:位,指0或1;byte:字节,1字节=8位。
- 预编译处理不是C语言的一部分,不占用运行时间,不需要加分号。
#define PI 3.1415926; // 错误!不要分号。 typedef 3.1415926 PI; // 正确!不是预处理过程,需要分号。
- 注释不是C语言的一部分,不占用运行时间,不可以嵌套。
- 逗号运算符的优先级最低,赋值运算符的优先级倒数第二。
- C语言编写的程序称为源程序。
- C语言书写格式是自由的,每行可以写多个语句,但是要注意分号。
- C语言程序从编写到执行的过程:编译(.c)、链接(.obj)、运行(.exe)。
- 【注】.c和.obj文件是不能运行的,只有.exe文件才可以运行。
2、标识符
- 合法的标识符必须由字母、数字、下划线组成,有其他元素就错了,并且第一个字符不能是数字。
- 分类:关键字、预定义标识符和用户标识符。
- 关键字:已被C语言使用,不能作其他用途使用的字,不能作为用户标识符。
- 【注】main、define、scanf、printf都不是关键字。
- If是关键字,因为If中的第一个字母是大写,所以不是关键字。
- 【注】main、define、scanf、printf都不是关键字。
- 预定义标识符:如:define、scanf、printf、include。可以作为用户标识符。
- 用户标识符:用户标识符是用户根据需要自己定义的标识符,一般用来给变量、函数、数组等命名。
- 关键字:已被C语言使用,不能作其他用途使用的字,不能作为用户标识符。
第二章 算法
1、基础知识
- 算法的特征:有穷性、确定性、有效性、有零个或多个输入、有一个或多个输出。
- 程序的三种基本结构:顺序结构、选择结构、循环结构。
- 算法可以没有输入,但是一定要有输出。
第三章 顺序程序设计
1、整型和浮点型
- C语言只有八、十、十六进制,没有二进制。但是运行的时候,所有的进制都需要转换为二进制来处理。
- 八进制:以0开头。如:017。
- 十六进制:以0x开头。如0x17。
- 【注】018是非法的,因为八进制没有8,逢8进1。
- 小数写法:C语言小数点两边有一个是零的话,可以不写。
- 1.0在C语言中可写成1.
- 0.1在C语言中可写成.1
- 浮点型写法:e前e后必有数,e后必须为整数。
- 如:2.333e4,表示为2.333×10⁴。
- 整型一般是4个字节,字符型是1个字节,双精度一般是8个字节(由具体的计算机字长决定)。
2、运算符和表达式
- 算术运算符:+、-、*、/、%。
- “/”两边都是整型,则结果就是整型。如3/2=1。
- “/”有一边是小数,则结果就是小数。3/2.0=1.5。
- “%”两边一定要是整数。
- 赋值表达式:从右向左赋值,常量不可以被赋值。
int x=y=10; // 错误!定义时,不可以连续赋值。 int x,y; x=y=10; // 正确,定义后,可以连续赋值。 int x=1.1; // 正确,x为7。 float=1; // 正确,x为1.0。
- 【注】赋值表达式的左边只能是一个变量。
- 复合赋值表达式
int a=2; a*=2+3; // 运行后,a的值为10。
- 【注】从右到左赋值,所以先计算2+3,再计算乘法赋值。
- 自增、自减运算符
- ++在前先加后用,++在后先用后加。
- --与上相同。
- 逗号表达式
- 逗号的优先级最低。
z=(2,3,4); // z的值为4。 z=2,3,4; // z的值为2。(z只能定义后才能这样赋值)
- 逗号的优先级最低。
3、强制类型转换
- 一定是(int)a,而不是int(a),类型上有括号。
- 注意(int)(a+b)和(int)a+b的区别。前者是把a+b的结果转换,后者是把a转换后+b。
- 三种取舍的情况:
int a=1.1; // 1 (int)a; 1/2; // 0
- 【注】都是直接取断,没有舍入。
4、四舍五入保留小数的方法
y=(int)(x*100+0.5)/100.0; // 保留两位小数,对第三位四舍五入。
y=(int)(x*1000+0.5)/1000.0; // 保留三位小数,对第四位四舍五入。
y=(int)(x*10000+0.5)/10000.0; // 保留四位小数,对第五位四舍五入。
- 【注】x=(int)x; 这是把小数部分去掉。
5、字符型
- 字符的合法表示形式
- '1'是字符占一个字节;"1"是字符串占两个字节,包括'\0'。
- 常见字符的ASCII码值:'0'为48,'a'为97,'A'为65。
- 字符可以进行算术运算,如'0'-0=48。
- 大小写字母的转换方法:'A'+32='a',之间相差32。
- 【注】字符表示的错误形式:'65',"1"。
- 转义字符
- 一般转义字符:\0、\n、\'、\"、\\。
- 八进制转义字符:'\17',即为八进制的17,前面的0不能写,与017等价。
- 十六进制转义字符:'\xee',即为十六进制的ee,前面的0不能写,与0xee等价。
- 字符型与整型的转换
char a=65; printf("%c",a); // a printf("%d",a); // 65
6、位运算
- 方法:把十进制转换为二进制计算,再转换为十进制。
- 位运算符:位与&、位或|、位异或^(同0异1)。
- 在没有溢出或舍入时,<<左移一位表示乘2;>>右移一位表示除2。
7、数据的输出
- 使用printf和scanf函数时,一定要加上头文件#include<stdio.h>。
- printf("第一部分",第二部分); 第二部分的值以第一部分的格式输出。
- 例:
printf("a=%d,b=%d",12,34); // 输出a=12,b=34
- 例:
- printf输出的数据由格式决定。
- 例:
int x=017; printf("%d",x); // 15 printf("%o",x); // 17 printf("%#o",x); // 017 printf("%x",x); // 11 printf("%#x",x); // 0x11
int x=12, y=13; printf("%d",x,y); // 输出12 //【注】只有一个格式说明,后面的y不输出。
- 例:
- 格式说明
格式说明 | 表示内容 | 格式说明 | 表示内容 |
---|---|---|---|
%d | int | %c | char |
%ld | long int | %s | 字符串 |
%f | float | %o | 八进制 |
%lf | double | %#o | 带前导的八进制 |
%5d | 输出5位数 | %x | 十六进制 |
%% | 输出% | %#x | 带前导的十六进制 |
printf("%2d",123); // 第二部分有三位,大于指定的两位,原样输出123
printf("%5d",123); // 第二部分有三位,小于指定的五位,左边补两个空格,故结果为 123
printf("%10f",1.25); // %f默认规定小数为6位,故结果为 1.250000
printf("%5.3f",1.25); // 小数三位,总共输出5位,结果为1.250(小数点算一位)
printf("%3.1f",1.25); // 小数一位,整个三位,结果为1.3(四舍五入)
//【注】这与强制类型转换不同,强制类型转换是直接截断,这是四舍五入。
8、数据的输入
- scanf("第一部分",第二部分); 必须以第一部分的格式输入数据。
- 例:
scanf("a=%d,b=%d",&a,&d); // 输入a=12,b=34才能把12和34正确赋值给a和b。
- 例:
-
int x,y; scanf("%d,%d",x,y); // 错误!scanf的第二部分一定要是地址。
- 指针在scanf中的应用
int x=2; int *p=&x; scanf("%d",p); // 正确 scanf("%d",x); // 错误! scanf("%d",&p); // 错误! scanf("%d",*p); // 错误!
- 指定输入的长度
- 终端输入:12345678
scanf("%2d%4d%d",&x,&y,&z); // x为12,y为3456,z为78(z把剩下的全包)
- 终端输入:1 234567,注意1和2之间有空格,所以只有1能给x
scanf("%2d%4d%d",&x,&y,&z); // x为1,y为2345,z为67
- 终端输入:12345678
- 输入时字符型和整型的区别
scanf("%d",&x); // 输入1,表示整数1。 scanf("%c",&x); // 输入1,表示字符'1',其ASCII为整数49。
9、putchar和getchar函数
char a=getchar(); // 从键盘得到一个输入的字符,包括空格。
putchar('y'); // 输出字符y。
第四章 选择结构程序设计
1、关系运算符和关系表达式
- 关系表达式的数值只能为1(真)或0(假)。
- 如:9>8为真,数值为1;9<8为假,数值为0。
- int x=1,y=0,z=2;
- x<y<z是真还是假?代入为1<0<2,从数学的角度看为假。
- 但是C语言是顺序运算的,1<0为假,得到0,0<2为真,得到1,故该表达式为真。
- 等号“==”;赋值“=”。
2、逻辑运算符和逻辑表达式
- 逻辑运算符:&&、||、!
- 优先级:! > && > ||
- 【注】在C语言中写x<y<z的时候,要写为(x<y)&&(y<z)。
3、if语句
- else是与最近且没有else的if语句匹配。
- 注意:if(a<b) t=a;a=b;b=t;和if(a<b){t=a;a=b;b=t;}的区别。(注意有无大括号)
- if语句的形式:
- 单独的if语句:
if(a<b) t=a;
- 标准的if语句:
if(a<b) t=a; else t=b;
- 嵌套的if语句:
if(a<b) if(a<c) t=a;
- 多选的if语句:
if(a==t) printf("a"); else if(b==t) printf("b"); else if(c==t) printf("c"); else printf("d");
- 单独的if语句:
- 条件表达式:表达式1 ? 表达式2 : 表达式3(真前假后)
- 表达式1为真,执行表达式2;表达式1为假,执行表达式3。
- 例:
k=1 > 2? 3: 4 > 5? 4: 5; // k为5
- switch语句
- 形式:
switch(a){ case 1: break; case 2: break; }
- 【注】switch中的数值一定为整型、字符型或枚举型,不能是浮点型。
- case中的数值一定是常量或常量表达式,不能是变量。
- 【注】switch中的数值一定为整型、字符型或枚举型,不能是浮点型。
- case中没有break时,只要有一个case匹配成功,剩下的语句都要执行。只要遇到break才能跳出switch,或执行完switch中的全部语句。
- switch只能与break一起使用,不能用continue。
- 形式:
第五章 循环结构程序设计
1、三种循环结构
- for()、while()、do-while();
- for( ; ; )条件中必须要有两个分号。
- 循环一定要有结束条件,不然就成死循环了。
- 注意do-while();循环最后while;的分号不能丢。
- do-while()循环至少执行一次。
2、break和continue
- break:跳出整个一层循环。
- continue:结束本次循环,即循环体内剩余的语句不再执行,直接判定执行下一次循环。
3、while语句循环接收字符
- 不停地输入,直到输入回车停止输入。
while((c=getchar()) != '\n')
- 注意while( (c=getchar() ) != '\n' )和while( c=getchar() != '\n' )的区别(有无括号)。
- 例:a=3!=2和(a=3)!=2的区别。
- 因为!=的优先级高于=,所以前者会先计算3!=2,故a=1;而后者a为3。(记住赋值的优先级为倒数第二)
- 例:a=3!=2和(a=3)!=2的区别。
4、取余
- 判断数值能否被整除:i%2==0,若为真,即为i能被2整除。
- 每行输出5个元素:
for(int i=1;i<=100;i++){ printf("%d",i); if(i%5==0) printf('\n'); }
- 逆序输出数字:
int i=123; while(i!=0){ printf("%d",i%10); i/=10; }
5、for只管后面的一个语句
for(int i=3;i>6;i++);
printf("#"); // 输出一个#
- 【注】for后有分号,表示for语句到这即为结束。
6、自减在循环中的运用
int k=1; while(--k); printf("%d",k); // k为0,先自减再判断
int k=1; while(k--); printf("%d",k); // k为-1,先判断再自减
第六章 数组
1、一维数组
- 一维数组的定义
int a[x]; // 数组中的x不能是变量,只能是常量 int a[5]; // 正确 int a[1+1]; // 正确 int a[1/2+4]; // 正确,可以为算术表达式 #define x 5 int a[x] // 正确,x为符号常量 int x=5,a[x]; // 错误!个数不能是变量
- 一维数组的初始化
int a[5]={1,2,3,4,5}; // 正确 int a[5]={1,2,3,}; // 正确,可以部分赋值 int a[]={1,2,3,4,5}; // 正确,后面的元素个数决定的前面的大小 int a[5]={1,2,3,4,5,6}; // 错误!赋值个数大于定义个数
- 对于a[10]
- a表示数组名,是第一个元素的地址,即a[0]的地址,等价于&a。
- a是地址常量,不能够被赋值,所以不可以执行a++操作。
- a是一维数组名,所以是列指针,a+1是跳过一列的元素,即跳过一个元素。
2、二维数组
- 二维数组的初始化
int a[2][3]={1,2,3,4,5,6}; //正确 int a[2][3]={1,2,3,4,5,}; // 正确,可以部分赋值 int a[2][3]={{1,2,3},{4,5,6}}; // 正确 int a[2][3]={{1,2,},{4,5,6}}; // 正确 int a[2][3]={1,2,3,4,5,6,7}; // 错误!赋值的个数大于定义的个数 int a[][3]={1,2,3,4,5,6}; // 正确,可以不写明行数 int a[2][]={1,2,3,4,5,6}; // 错误!一定要写明列数
- 对于a[3][3]
- a表示数组名,是第一个元素的地址,即a[0][0]的地址。
- a是地址常量,不能够被赋值,所以不可以执行a++操作。
- a是二维数组名,所以是行指针,a+1是跳过一行的元素。
- a[0]、a[1]、a[2]是地址常量,表示每行元素的起始地址,不能够被赋值。且它们都是列指针,a[0]+1、a[1]+1、a[2]+1是跳过一列的元素,即跳过一个元素。
- 基础知识
- 赋值顺序为先行后列。
- *(a[0]+1)表示第一行第一个元素的后一个元素,即a[0][1]。
- 二维数组初始化时,一定要标明列数。
- a[2][3],即*(a+2)[3],即*(*(a+2)+3)。
- 赋值顺序为先行后列。
第七章 函数
1、基础知识
- 函数是具有一定功能的一个程序块,是C语言的基本组成单位。
- 函数不可以嵌套定义,但是可以嵌套调用。
- 函数缺省返回值类型时,默认为int。
- C语言由函数组成,并且有且只有一个main函数,是程序运行的开始。
2、常用函数
- 判断数值是否为质数(素数)
int iszishu(int a){ for(i=2;i<a/2;i++) if(a%i==0) return 0; // 不是素数 return 1; // 是素数 }
- 求阶层n!
int fun(int n){ int p=1; for(i=1;i<=n;i++) p*=i; return p; }
3、函数的参数可以是常量、变量、表达式和函数调用
int add(int x,int y){ return x+y; }
void main(){
int sum;
sum=add(add(7,8),9); // sum=24
}
4、函数声明
- 声明函数时,一定要有函数名、函数的返回值类型、函数的参数类型。不一定要有形参名。
- int *fun(int a[],int b[]){ ... }函数声明的写法:
int *fun(int *a,int *b); // 数组就是指针 int *fun(int a[],int b[]); int *fun(int x[],int y[]); // 参数名称可以不同 int *fun(int *,int *); // 参数名称可以省略
5、指针函数
- 指针函数是指其返回类型是某一类型的指针。
int *fun(int *a,int *b){ if(*a>*b) return a; else return b; } void main(){ int x=1,y=2,*max; max = fun(&x,&y); printf("%d",*max); // *max为2 }
- 【注】int *fun()和int (*fun)()的区别。前者是指针函数,后者是函数指针。
6、常用的库函数
- #include<math.h>中:
- abs():整型绝对值
- sqrt():开根号
- fabs():字符型绝对值
- pow():pow(2,3)即为2的3次方
- #include<string.h>中:
- strcmp():比较
- strcat():连接
- strcpy():复制
- strlen():长度
- 【注】使用这些库函数一定要加上头文件。
第八章 指针
1、指针
- 指针变量用来存放地址,一般变量用来存放数值。
- 指针变量的初始化
int a=2, *p=&a; // 定义时初始化 int a=2,, *p; p=&a; // 定义后初始化
- int *p中*p和p的区别
- *p是数值,可以当作变量来用,*的作用是取地址p中的数值。
- p是地址,可以当作地址使用,scanf("%d",p);
- *p++和(*p)++的区别
- *p++是地址自增。
- (*p)++是数值自增。
- 例:int *p,a[]={1,3,5,7,9}; p=a;
- *p++为3;(*p)++为2。
- 移动指针
char *s="abcdef"; while(*s){ printf("%c",*s); // 循环打印字符 s++; // 移动地址 }
2、二级指针
- 一级指针*p:存放变量的地址。
- 二级指针**q:存放一级指针的地址。
int x=7; int *p=&x; int **q=&p; // *p为7,*q为p的地址,**q为7 **q=&x; // 错误!
-
【注】二级指针只能存放一级指针的地址。
-
3、三名主义
- 数组名:表示数组中第一个元素的地址。数组名不可以自增,它是地址变量。
int a[]={1,2,3,4}; a++; // 错误! a+=1; // 错误! int *p=a; p++; // 正确。指针可以自增
- 【注】数组名不能被重写赋值。
- 函数名:表示该函数的入口地址。
- 字符串常量名:表示字符串中第一个字符的地址。
4、传数值和传地址
- 传数值
void fun(int a,int b){ int t; t=a; a=b; b=t; } void main(){ int x=1,y=2; fun(x,y); printf("%d,%d",x,y); // 输出1,2 }
- 【注】传数值,形参变化不会改变实参的值。
- 传地址
void fun(int *a,int *b){ int t; t=*a; *a=*b; *b=t; } void main(){ int x=1,y=2; fun(&x,&y); printf("%d,%d",x,y); // 输出2,1 }
- 【注】传地址,形参变化会改变实参的值。
第九章 数据类型
1、结构体
- 结构体是由不同类型数据组成的组合型的数据结构。
- 声明一个结构体类型的一般形式:
struct Student{ int num; char name[20]; };
- struct Student为结构体类型,Student为结构体名。
- 定义结构体类型变量:
struct Student student1,student2; student1=student2; // 同类型的结构体变量可以相互赋值。
- 成员可以属于另一个结构体类型。
struct Date{ int month; int day; }; struct Student{ int num; char name[20]; struct Date birthday; };
- 结构体类型的初始化:
struct Student{ int num; char name[20]; }s={1,"zz"}; // 定义时初始化 struct Student s={1,"zz"}; // 定义后初始化 struct Student s={.num=1}; // 对某一个成员初始化 struct Student s[2]={1,"zz",2,"xx"}; // 定义结构体数组 struct Student s[2]={{1,"zz"},{2,"xx"}}; // 也可用大括号包起来
- 结构体变量中成员的引用:
struct Student{ int num; char name[20]; }s={1,"zz"}; s.name="xx"; // 错误!不能利用数组名赋值 printf("%d",s.num); // 正确 printf("%s",s); // 错误!不能用结构体变量名输出所有成员的值 scanf("%d,%s",&s); // 错误!不能这样对所有成员赋值 scanf("%d,%s",&s.num,s.name); // 正确
- 指向数据结构体数组的指针:
struct Student s[2]={1,"zz",2,"xx"}; struct Student *p=s; printf("%d",(*p).num); // 括号不能省略,成员运算符“.”优先于“*”运算符 printf("%d",p->num);
2、共用体
- 共用体是用同一段内存单元存放不同类型的数据,使用覆盖技术,后一个数据会覆盖前面的数据,即每一瞬间只能存放一个成员,而不能同时存放几个。
- 共用体的大小由长度最大的成员决定。
- 共用体的声明定义
union Data{ int i; char ch; float f; }a,b,c; // 声明时定义变量 union Data a,b,c; // 声明后定义变量
- 共用体变量的引用
union Data a,b; printf("%d",a.i); // 正确 printf("%d",a); // 错误!不用只引用共用体变量 a=b; // 正确,同类型的共用体变量可以相互赋值 a=97; //错误!不能对共用体变量名赋值 a.i=97; // 正确 printf("%d",a.i); // 输出整数97 printf("%c",a.ch); // 输出字符'a' printf("%f",a.f); // 输出实数0.000000
3、枚举类型
- 枚举就是将可能的值都列举出来,变量的值只限于列举出来的值的范围内。
- 例:
enum Weekday{sun,mon,tue,wed,thu,fri,sat}; enum Weekday workday,weekend; // workday、weekend为枚举变量 sun、mon...为枚举元素或枚举常量 workday=mon; // 正确 weekend=sun; // 正确 weekday=monday; // 错误!monday不是枚举变量之一 sun=0; mon=1; // 错误!不能对枚举元素赋值
- 每一个枚举元素都代表一个整数,默认为0,1,2,3,4...
- workday=mon;相当于workday=1;
enum Weekday{sun=7,mon=1,tue,wed,thu,fri,sat}workday,weekend; // 指定sun的值为7,mon为1,之后顺序加1,sat为6
- 枚举元素可以用来作判断比较。例:
if(workday==mon) if(workday>sun)
- workday=mon;相当于workday=1;
4、typedef
- 用typedef指定新的类型名来代替已有的类型名。
- 例:
typedef int Integer; // 记住由分号 int i; Integer i; // 与上相同 // 命名新的类型名代表结构体类型 typedef struct{ int month; int day; }Date; Date birthday;
-
【注】#define是在预编译时处理的,而typedef是在编译阶段处理的。
第十章 文件的输入输出
1、打开与关闭文件
- 使用fopen和fclose打开和关闭文件
FILE *pf; if((fp=fopen("file1","r"))==NULL){ printf("cannot open this file\n"); exit(0); // 退出,需要加头文件#include<stdlib.h> } fclose(fp); // 一定要关闭,因为只有缓冲区满了才会把数据传输到磁盘。 // 如果当数据未充满数据区时程序结束运行,就可能使缓冲区的数据丢失
- 使用文件方式
文件使用方式 | 含义 | 如果指定的文件不存在 |
---|---|---|
r(只读) | 为了输入数据,打开一个已存在的文本文件 | 出错 |
w(只写) | 为了输出数据,打开一个文本文件 | 建立新文件 |
a(追加) | 向文本文件尾添加数据 | 出错 |
rb(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
wb(只写) | 为了输出数据,打开一个二进制文件 | 建立新文件 |
ab(追加) | 向二进制文件尾添加数据 | 出错 |
r+(读写) | 为了读和写,打开一个文本文件 | 出错 |
w+(读写) | 为了读和写,建立一个新的文本文件 | 建立新文件 |
a+(读写) | 为了读和写,打开一个文本文件 | 出错 |
2、读写文件
- fgetc和fputc
fgetc(fp) // 从fp指向的文件读入一个字符 fputc(ch,fp) // 把字符ch写到文件指针变量fp所指向的文件中
- fgets和fputs
fgets(str,n,fp) // 从fp指向的文件读入一个长度为(n-1)的字符串,存放到字符数组str中 fputs(str,fp) // 把str所指向的字符串写到文件指针变量fp所指向的文件中
- 用格式化的方式读写文本文件
// fprintf(文件指针,格式字符串,输出表列); fprintf(fp,"%d,%f",i,f); // fscanf(文件指针,格式字符串,输入表列); fcanf(fp,"%d,%f",&i,&f);
- 用二进制方式向文件读写一组数据
fread(buffer,size,count,fp); fwrite(buffer,size,count,fp); // buffer:是一个地址,用来存放从文件读入的数据的地址或把该地址的数据写入文件中 // size:要读写的字节数 // count:要读写多少个数据项 // fp:文件指针
- 判断文件是否读取完
feof(fp); // 结束返回真,未结束返回假