C语言知识点总体梳理
一、数据的表现形式
1、常量
(1)整型常量
(2)实型常量:十进制小数形式(由数字和小数点组成)、指数形式(12.34e5,e或E之前和之后必须有数字、之后的数字必须是整数)
E表示10的多少次方,次方跟在E后
(3)字符型常量:普通字符(单引号括起来的单个字符,以ascii码值存放,)、转义字符(以\开头的字符序列P40)
(4)字符串常量:双引号括起来的任意字符
(5)符号常量:用#define指令,指定用一个符号名称代表一个常量,好处见名知意、一改全改,注意符号常量不占内存,编译后,符号不存在
#define PI 3.1416 //行末没有引号
案例:区分1、‘1’、“1”,分别是整形常量 、字符常量 、字符串
2、变量
(1)先定义后使用
(2)变量有三要素,变量名,变量地址,变量的值
(3)标识符(变量的名字):字母、数字、下划线,不能以数字开头,区分大小写,自定义的标识符不能是关键字
二、数据类型
数据类型(决定了数据占用的内存空间及存储形式,决定了数据可以参与的运算,C标准并没有规定每种类型占用的字节数,由编译系统自行决定(P45)、sizeof运算符,可以测量变量或类型的长度,占用的字节数,决定了可以表示数的范围)
1、各种数据类型
(1)整型
(2)字符型:字符型在一定范围内可以和整型通用(P48)
(3)实型(浮点型) :实数以指数形式存储
注意:只有整型、字符型可以使用signed或unsigned修饰,实型不可以,无符号整型用%u输出
用有限的单元不能完全精确地存储一个实数
2、确定常量的类型
2----int
2L----long
3.4----double
3.4f----float
3、数据的输入输出(C语言本身不提供输入、输出操作,由库函数完成)
(1)输入
1) int scanf(char * format,args,…);
scanf(“a=%d”,&a); (P75注意事项)
char * 相当于字符串格式,用字符数组进行存放----char ch[10]=“a=%d”;
格式控制字符串:以%开头的格式控制符(P74,与输入项对应),以及其他普通字符(原样输入)
读入double类型的数值的时候后面别忘了加f
输入项(地址表列):变量的地址
2) int getchar(); ------从标准输入设备输入一个字符 ,空格、回车、tab等都是有效字符
char c=getchar()
#include <stdio.h>
#define SIZE 10
void findmax( int *px,int n,int *pmax );
int main()
{
int a = getchar();
printf("a=%d\n",a);
char c = getchar();
printf("c=%c\n",c);
}
如果输入的是数字,自动转换为字符型
(2)输出
int printf(char * format,args,…) 返回输出字符的个数
printf(“a=%d”,a);
格式控制字符串:以%开头的格式控制符(与输出项对应),以及其他普通字符(原样输出)
输出项:变量
(3)int putchar(char ch); 输出一个字符
putchar(getchar())
char c=’y’;
putchar©;
#include <stdio.h>
void findmax( int *px,int n,int *pmax );
int main()
{
int a = getchar();
putchar(a);
char c = getchar();
putchar(c);
printf("\n");
}
4、sizeof的使用
#include <stdio.h>
#define SIZE 10
void findmax( int *px,int n,int *pmax );
int main()
{
printf("一个int类型的值所占的字节为:%lu\n",sizeof(int));
printf("一个float类型的值所占的字节为:%lu\n",sizeof(float));
printf("一个char类型的值所占的字节为:%lu\n",sizeof(char));
printf("一个double类型的值所占的字节为:%lu\n",sizeof(double));
int a[5];
printf("一个int类型的数组:a[5]所占的字节数为:%lu\n",sizeof(a));
printf("a数组的长度为:%lu\n",sizeof(a)/sizeof(int));
double b[3][4];
printf("一个double类型的二维数组:b[3][4]所占的字节数为:%lu\n",sizeof(b));
printf("b数组的长度为:%lu\n",sizeof(b)/sizeof(double));
}
三、运算符(对数据进行加工)运算规则、优先级结合性、表达式的值
1、算术运算符
1) 算术运算符:+、-、*、/(两个整数相除结果为整数)、%(操作数必须为整数)、++、 – (只能用于变量,分清前缀后缀)
int a=3,c;
c=a++; c= 3 a=4 ==>c=a; a++
c=++a; c=4,a=4 ==>a++ ;c=a
2.5/2=1.25
5/2=2
(a+b)++ 错误
2) 结合性:自左至右
3) 算术表达式的值:计算结果
2、赋值运算符
1) 基本赋值运算符=,将右侧的值赋给左侧的变量
int a,b; a+b=5(错误)
2) 复合赋值运算符,例如+=
a+=b<===> a=a+(b)
括号不要忘记 a一定是变量,b是表达式(一个变量或常量是最简单的表达式)
int a=2,b=3,c;
c*=a+b; ===>c=c*(a+b) 一定别忘记括号
3) 结合性:自右至左
int a,b,c=8;a=b=c ===>a=(b=c)
int a,b,c; a=b+1=c+2;===>a=(b+1=c+2) 错误
int a=b=3; 错误,b未声明
int a=3,b=3; 或 int a,b; a=b=3;正确
4) 赋值表达式的值:赋值运算符左侧变量的值
5) 赋值运算符两侧的类型转换:两侧的类型都是数值型,以左侧变量的类型为准,由系统自动完成
例如
double a=1; ==>a=1.0
int a=3.4; ==>int a=(int)3.4 -->a=3(有精度损失的警告,但不影响运行,但在java中报编译错误)
3、关系运算符
1)符号 、!、=、 >、 <、 >=、 <=
2)运算规则 注意区分=和==
3)结合性:自左至右
4)关系表达式的值 1(表达式成立,真)或0(表达式不成立,假)
int x=5;
10<=x<=99 关系表达式自左至右求解,先求10<=x,则表达式不成立表达式的值为0 ,接下来继续求解0<=99 ,结果为1
不论x的值是多少,表达式10<=x<=99的值恒为1
4、逻辑运算符
1)符号 &&逻辑与、 ||逻辑或(双目运算符)、 !逻辑非(单目运算符)
2)运算规则 (a,b可以为一个常量或变量或表达式)
a&&b,a,b两者同为真(非零为真,否则为假)时,结果为真
a||b,a,b同时为假,结果为假,否则结果为真
!a,取反,a为真,结果为假,a为假结果为真
系统如何判断一个量的真假:非零为真,0为假
系统如何表示真假:1–真 0----假
3)结合性:自左至右
4)逻辑表达式的值为1或0,成立(真)为1,否则为0
a=3,b=4,c=5
((a+b)>c)&&(b==c) | 逻辑表达式 值为0 |
---|---|
a||((b+c)&&(b-c)) | 短路现象 1 |
((!(a>b))&&(!c))||1 | 不存在短路现象 1 |
(!(x=a))&&(y=b)&&0 | 短路现象 0 |
((!(a+b))+c-1)&&(b+c/2) | 1 |
5)短路现象:存在于&&和||中,
a&&b,当a为假的时候,不需要计算b的值,就可以直接得到整个逻辑表达式的值为假,这个现象称为短路现象
a||b,当a为真的时候,不需要计算b的值,就可以直接得到整个逻辑表达式的值为真,这个现象称为短路现象
例如
a=3,b=4,c=5
x=1,y=2则执行下面的表达式后,x=3,y=2(短路现象)
(!(x=a))&&(y=b)&&0
5、条件运算符
符号表达式1?表达式2:表达式3 二分支结构
运算规则,求解表达式1,为真则求解表达式2,并将表达式2的结果作为整个条件表达式的结果,否则求解表达式3,并将表达式3的结果作为整个条件表达式的结果
等价于二分支结构
if(表达式1)
表达式2
else
表达式3
6、逗号运算符 ,
1)规则:自左至右求解,并将最后一个表达式的值作为整个逗号表达式的值
7、间接引用运算符*:后面只能跟地址,表示的是地址指向的内存
int a; ---------- *&a<===>a
8、地址运算符 &:后面只能跟变量
int a;int *p=&a; &a=====p 均指整型变量a的地址
int *p; &p-----代表指针变量的地址,合法
三、流程控制
1、顺序
2、选择(分支)
(1)if语句
语法:
if(表达式)
语句
注意:
1) 表达式是任意合法的表达式,表达式后不能加分号
2) 语句为一条语句,如果有多条,需要用大括号构成复合语句
3)执行:如果表达式成立(为真,非零为真),则执行语句
(2)if…else语句
语法:
if(表达式)
语句1
else
语句2
注意:
1)表达式是任意合法的表达式
2)语句1、2为一条语句,如果有多条,需要用大括号构成复合语句
3) else表示的情况与if完全相反,并且else必须和if配对使用
4) 执行:如果表达式成立(为真,非零),则执行语句1,否则执行语句2
案例,else必须和if配对
程序报错
else总是和离它最近没有配对的if配对,可以用大括号改变配对(P108,第七题),else不能单独使用
(3)if语句的嵌套
(4)switch 语句—多分支语句(开关语句)
语法:
switch(表达式)
{
case 常量表达式1:语句1;
case 常量表达式2:语句2;
case 常量表达式3:语句3;
….
default:语句n;
}
注意:
1)表达式可以为任意合法的表达式,但其值必须为整型(字符型)
2)case后面必须是常量表达式,并且不能重复
3)case后面的语句可以有多条,不必加括号
4)执行:首先计算表达式的值,然后将其和case后面的常量比对,如果相等,则执行case后面的语句,一直到结束,除非遇到break语句,结束swtich语句
5)多个case可以共用一条语句
6)break ,default不是必须的
3、循环结构——重复,次数确定或不确定
循环变量的初始值、循环条件、循环体(一条语句,循环体中有使循环条件趋于不成立的语句)
(1)While语句
语法
while(表达式)//循环条件,后面没有分号
{
语句 //循环体,包含使循环条件不成立的语句
}
注意:
1)表达式为任意合法的表达式,当表达式为真时,执行循环体,当表达式为假,结束循环,循环体最少执行0次
(2)do…while语句
语法:
do
{
语句//循环体,包含使循环条件不成立的语句
}while(表达式); //循环条件,后面有分号
注意:
1)表达式为任意合法的表达式,先执行语句,然后判断循环条件,当表达式为真时,继续执行循环体,当表达式为假,结束循环,循环体至少执行1次
(3)for语句
语法:
for(表达式1;表达式2;表达式3)//后面没有分号
{
语句
}
注意:
先执行表达式1,然后判断表达式2,如果为真则执行语句,然后执行表达式3,如果为假则结束循环
三个表达式都可以省略,分号不可以省略
表达式1通常为循环初始条件,表达式3通常为循环变量的改变
4、break\continue
break 结束其所在的循环
continue结束本次循环,接着进行下一次循环条件的判断
continue在for循环中会接着执行表达式3,然后判断循环条件
典型案例:求和、计数器、分离各个位数字、素数、斐波那契数列、每行输出几个数、输入一行数以#号结束(P136)、菱形图案、判断几位数
四、数组 批量同类型数据,数组元素在内存中连续存放,数组名代表数组首地址,是一个地址常量、构造类型(根据一定的规则,利用基本类型构造而成)
1、一维数组
(1)数组声明
语法:
类型 数组名[常量表达式];
注意:
1) 类型是任意合法的类型
2) []里一定是常量,代表数组元素的个数
(2)数组初始化(定义的同时赋值)
Int a[10]={1,2,3,4}//可以全部赋值,可以部分赋值(其余元素都是默认值),如果全部赋值,则数组大小可以省略
例如:Int a[]={1,2,3,4}等价于int a[4]={1,2,3,4}
注意:除了初始化时可以给所有元素赋值,否则,定义完数组时,只能逐个元素赋值
Int a[10];//10代表是数组元素的个数
a[10]={1,2,3,3};//错误10代表数组元素的下标
a={1,2,4};//错误
(3)数组元素的引用
语法:
数组名[下标]
注意:
不能一次性引用数组元素,只能逐个引用,下标从0到数组大小减1,通常利用循环遍历数组
(4)常见算法
遍历、最值、排序、逆置、增删改查
2、二维数组:一维数组的数组
存放批量同类型数据,数组元素在内存中连续存放(按行存放,第一行存放完再存放第二行…),数组名代表数组首地址,是一个地址常量
(1)数组声明
语法:
类型 数组名[常量表达式] [常量表达式];
注意:
1)类型是任意合法的类型
2)[]里一定是常量,代表数组元素的个数
(2)数组初始化(定义的同时赋值)
Int a[10][3]={{1,2,3,4},{}}//可以全部赋值,可以部分赋值(其余元素都是默认值),如果全部赋值,则数组第一维大小可以省略
例如:
Int a[2][3]={1,2,3,4,5,6}
等价于a[][3]={1,2,3,4,5,6}
注意:除了初始化时可以给所有元素赋值,否则,定义完数组时,只能逐个元素赋值
(3)数组元素的引用
语法
数组名[行标][列标]
通常用双层循环遍历二维数组,外层控制行,内层控制列
(4)常见算法
遍历、最值、转置(行列互换)、上下三角
3、字符数组:存放字符
(1)数组声明语法:
char 数组名[常量表达式];
(2)初始化
Char a[10]={‘h’,’i’}//可以全部赋值,可以部分赋值(其余元素都是默认值),如果全部赋值,则数组大小可以省略
Char ch[]={“hello”} ==>char ch[]=”hello”//字符串结束标志\0 数组ch长度为6(包含\0)
字符串结束标志\0,但字符数组可以不包含\0
(3)数组元素引用
数组名[下标]
(4)字符数组的输入、输出
C对于字符串(双引号括起来的若干个字符),自动追加\0代表字符串结束标志,所有的字符串一定放在字符数组中存放
1) 逐个字符输入、输出 %c
2) 以字符串的形式一次输入、输出 %s
例如
char ch[10];
scanf(“%c”,&ch[0]);//%c逐个输入
scanf(“%s”,ch) //输入项为字符数组名,输入的字符串中不能包含空格(空格,回车,tab为字符串结束标志)
printf(“%c”,ch[0]);
printf(“%s”,ch);//输出字符串,一直到遇到第一个\0
(5)字符串处理函数
puts 输出字符串(\0结束的字符序列),将\0转换为换行,即输出字符串后换行------stdio.h
int puts(char * str)
puts(“strss”);
gets 输入一个字符串------stdio.h
char *gets(char *str); 输入一个字符串存放在str指向的内存中,同时返回这个地址
strcat---------字符串连接 string.h
Char * strcat(char *str1,char * str2 ) //str2连接到str1后面,返回str1
注意:str1指向的内存空间要足够大
strcpy -----字符串复制string.h
char * strcpy(char *str1,char *str2);//str2复制到str1,返回str1
Str1指向的空间应该足够大
char ch1[]=”hello”;
char ch2[];
ch2=ch1;//错误
Char * Strncpy(char *str1,char *str2,int n);//将str2前n个字符复制到str1
strcmp-----字符串比较函数 string.h
Strlen----字符串长度
Strlwr------转换为小写 char *strlwr(char *s)
Strupr------转换为大写
五、函数 (function)—完成一定功能的模块(算法),实现代码的复用
1、 函数的定义--------- 不可以嵌套定义,可以嵌套调用
1)函数的分类 库函数----自定义函数
从函数的形式分,分为有参函数和无参函数
2)函数的定义:分为函数首部(函数头),函数体两部分
语法:
返回值类型(函数类型) 函数名(形参表列)--------函数首部
{
说明部分(变量定义或函数声明) --------函数体
执行部分(语句)
}
注意:
返回值类型(函数的输出)的确定,首先判断函数是否需要返回一个值,如果需要再看返回值的类型,如果没有返回值,则明确声明为void,一定注意,返回值只能有一个,如果多于一个,只能通过别的方式带回,此时也将函数返回值类型声明为void
函数名:一个合法的标识符,尽量见名知意,多个单词连用,通常除了第一个单词外,其余单词首字母大写
形参表列:形参1类型 形参1名,形参2类型 形参2….,一个函数可以有多个形参,形参之间用逗号隔开,形参名是任意合法的标识符。是函数完成其功能时,所需要的必要的已知条件,相当于函数的输入,如果函数不需要已知条件,则空着,此时是无参函数。
函数体,是函数功能的具体实现(算法的具体实现),此时形参相当于已经有值(在函数调用时,由实参传递过来的值),不需要重新赋值,如果函数需要返回一个值,用return 语句带回(return语句将后面表达式的值带回到函数调用处,并结束函数的调用,return后面可以不加表达式,此时仅仅结束函数的调用,return语句可以有多个,但只有一个会执行)
例如:
1、 求两个数的和
double add(double x,double y)
{
return x+y;
}
2、 求两个整数的最大值
int max(int x,int y)
{
return (x>y)?x:y;
}
3、 求1-100的和
int sum()
{
int s=0,i;
for(i=1;i<=100;i++)
s+=i;
return s;
}
4、 求两个整数的最大公约数
int fun(int x,int y)
{
}
5、 求两个整数的最小公倍数
int fun2(int x,int y)
{
}
6、判断一个数是否是素数
int isSushu(int x)
{
}
2、函数的调用
语法:
函数名(实参表列)
注意:
实参表列,实参可以是常量,变量或表达式。要求实参的个数和形参一致,类型和形参赋值兼容(形参1=实参1),实参必须有确定的值 i
如果要使用函数的返回值,可以定义一个和函数返回值类型一致的变量,接收返回值
案例,调用上面写的函数
1、 double s=add(3.5,5.6);//用常量做实参
或者
double x=3.5,y=5.6;
double s=add(x,y);//注意x,y是变量做实参,但x,y已经有确定的值
add(x+y,xy)//表达式做实参,求x+y和xy的和
add(a,add(a,b))add(a,b)&&1
注意:
1、 在发生函数调用时,首先程序执行的流程转到被调用函数,会给被调用函数的局部变量(定义在函数内部的变量,包括形参)分配内存空间,会将实参的值对应地传给形参
2、 在函数调用结束时(return语句或遇到函数体结束的右括号),程序执行的流程转到主调函数中函数调用处,回收被调用函数的局部变量(定义在函数内部的变量,包括形参)所占内存空间,如果需要return返回1个值,则把这个值带回函数调用处
因此,除了main函数外,其他函数中的局部变量只在被调用期间占有内存空间,不同函数的局部变量可以重名,仅仅是重名,一定占用不同的内存空间
3、函数调用时的参数传递
(1)普通变量(包括数组元素)做函数参数 由实参将值对应地传递给形参,传值调用(形参的改变不会影响实参)
(2)数组名做函数参数,由实参将值(地址)对应地传递给形参(传址调用)数组名就相当于一个指针
案例,函数完成数组排序:
void sort(int a[4]) ===》 void sort(int a[]) ====》 void sort(int *a)
如果指针指向数组,则指针变量可以带下标,此时指针变量相当于数组名
{
int i,j,t;
for(j=10;j>1;j--)
for(i=0;i<j-1;i++)
{
if(a[i]>a[i+1])
{
t=a[i];
a[i]=a[i+1];
a[i+1]=t;
}
}
}
4、函数的声明
编译系统自上而下编译代码,首先检查语法问题,没有问题则生成.obj文件,如果没有进行函数声明,编译系统自动地认为函数的类型(返回值的类型)是int型
以下代码中,f函数的类型为void,并且写在主调函数后,运行时报错,type mismatch in redeclaration of ‘f’,因此如果被调用函数如果写在主调函数后,且函数类型不是int,则必须添加函数声明
例如以下代码,在主调函数中并没有对被调用函数进行声明,程序可以正常执行,但此时存在风险 ,将下面红色字体的代码变成printf(“%d”,max3(x,y));时程序不报错,但运行结果不对,如果有函数声明,则此时红色字体的代码变成printf(“%d”,max3(x,y));时程序会报错 too few parameters in call to max3 in function main
函数声明(函数原型声明):
当被调用函数写在主调函数之后,此时可以在主调函数中,对被调用函数进行声明,方便编译系统检查函数调用表达式的正确性(检查项包括函数的类型、函数名字、参数的类型和个数)。
如果被调用函数写在主调函数之前,此时函数声明可以省略。
函数声明的语法: 函数返回值类型 函数名(形参1的类型,…);
或者函数返回值类型 函数名(形参1的类型 形参1,形参2类型 形参2);