课程地址:https://www.bilibili.com/video/av8074534?from=search&seid=17749628941038263826
一、C语言概述
二、C语言实例程序
三、C语言的起源和发展
1、程序语言的发展
Fortran语言主要用于计算;Basic语言目前没有大量使用,转化为VB;Pascal语言主要用于教学。c语言在结构性语言中是非常重要的,是结构化的代表。
因为结构化语言有缺陷,所以出现面向对象语言,C++是面向对象中最复杂最重要的语言,Java也是从中发展出来的。
Java的运行慢,但是任何机器上都可以运行。C语言运行最快,c++其次。
B语言是首先创造第一个Unix系统,一般用于大型商务机,该系统非常稳定。
C语言必学:函数、指针。
2、发展过程
四、C语言特点
1、优点
代码量小、速度快、功能强大。
1)、wps由金山公司设计,由C语言设计,代码容量小。
2)、操作系统:Windows(内层是C语言写的和外层是c++写的)、unix(C语言写的)、Linux(C语言写的)。操作系统速度要保证快且在运行软件时也要快,Java的运行慢不可以用来写操作系统。
3)、操作系统运行软件也可以操作硬件,所以C语言编写的操作系统功能强大。
2、缺点
危险性高、开发周期长、可移植性弱
1)、C语言的查错性很差,对于小错误不报,导致很多问题
没有被屏蔽。
2)、当代码很长时看起来很难找到结构。
3)、Java移植性强,各个机器执行的结果不同,导致差错。
五、C语言的应用领域
六、C语言的重要性
七、如何学习C语言
理论+实践
谭浩强C语言和c++入门很好。
逐步加难加深
八、C语言学习目标
九、常见问题
C语言是面向过程;Java是面向对象。
Java中引用的本质就是C语言中的指针。
十、课程计划
十一、举例:一元二次方程
1、C语言基本框架格式:
2、代码步骤
1)、定义变量,将方程的三个系数保存在计算机中;定义bb-4a*c判断根的个数;定义存放方程的结果值的变量。
2)、判断根的情况来来求解。
注意:sqrt()用于求平方根的函数;并且此工具在math.h文件中,所以开头要写:
control+alt+delete跳出任务管理器,关闭死机程序
代码:
int main(void)
{
//把三个系数保存到计算机中
int a=1;
int b=2;
int c=8;
//定义判断方程值的个数的变量
double delta;//b*b-4*a*c
//定义方程的解的变量
double x1;
double x2;
delta=b*b-4*a*c;
//判断方程的根值的情况
if(delta>0)
{
x1=(-b+sqrt(delta))/(2*a);
x2=(-b-sqrt(delta))/(2*a);
printf("该一元二次方程有两个实根:x1=%d\n,x2=%d\n",x1,x2);
//f表示输出的数据类型;\n表示换行
}
else if(delta==0)
{
x1=x2=(-b)/(2*a);
printf("该一元二次方程有两个相等的实根:x1=x2=%d\n",x1);
}
else{
printf("方程无解");
}
return 0;
}
十二、软件的使用
规范代码
命名规范
保存时保存源文件即可
十三、回顾本讲内容
十四、C语言预备基础知识
十五、cpu、内存条、硬盘、显卡、主板、显示器 之间的关系
1、数据本来是存放在硬盘上,当鼠标对一部影片进行双击时,系统就将硬盘上存放的影片的数据调入内存条,将影像传给显卡,声音传给声卡;最终通过显示器显示出来。
2、cpu、内存条、硬盘、显卡都是放在主板上的,主板是一个中间介质,提供场所的设备。
十六、HelloWorld程序是如何运行起来的?
编译生成exe程序,cpu执行exe文件得到结果,显示器显示结果。
windows2000以前软件可以操作硬件。
1、先编译:通过编译软件生成了一个可执行的.exe文件。
2、请求操作系统执行操作:执行exe文件是用操作系统的cpu执行的,并非软件执行;因为所有的软件都是运行在操作系统上的,都是由操作系统调动cpu来执行操作的。
3、软件程序不可以直接控制硬件,硬件由操作系统来控制。
十七、什么是数据类型
1、基本类型数据
1)整数
整型:int 即只能存放整数,例如:int i=10; 四个字节
短整型:short 两个字节
长整型:long 六个字节
2)浮点数【实数】
单精度浮点数:float 四个字节
双精度浮点数:double 八个字节
3)字符
单字符:char 一个字节
字符串:一系列字符的组合
2、复合类型数据(即基本数据类型的拼接)
结构体
枚举
共用体
十八、什么是变量
定义变量,将值赋值给变量,此时将数据存放在了内存中,程序终止之后,数据所占的空间被释放。
变量方便我们存储数据。
变量的本质就是内存中的一段存储空间。
int i; 定义一个i变量,当写这条语句时,变量会在内存条中去寻找一个空余的位置,与该内存空间产生关联,当存放变量时就指定存放在这个找到的位置。
i=3; 将3存入变量指定的内存空间中。
十九、cpu 内存条 c++软件 操作系统 之间的关系?
我们在c++软件中编写程序;
我们通过变量将数据存放在内存条中;
c++发送需要执行的请求;
操作系统分配内存;
cpu执行c++软件发出的请求。
使用变量实际上就是使用变量对应的内存空间。
二十、变量为什么要进行初始化?
所谓初始化就是赋值的意思。
内存条为硬件设备,默认的是0或1,表示低电平和高电平,内存空间可以不存放数据,但是默认是有东西的,而不是空白的,可能是以前释放空间的垃圾值,也可能是系统存放的值。
当软件发现没有进行初始化就调用变量的值时,即此内存空间内的值为垃圾值,就会默认存放一个以8开头的很大的固定的值来填充,称为添充字。
二十一、如何定义变量
数据类型 变量名 = 要赋的值;
等价于:
数据类型 变量名;
变量名 = 要赋的值;
举例:
int i=3, j=2;
int i; i=3;
int i, j=3; i=2;
int i, j; i=j=5;
二十二、什么是进制
十进制: 逢十进一
二进制: 逢二进一
n进制:逢n进一
二进制 B;八进制O;十进制D;十六进制H;
printf的用法:
进制举例:
二十三、常量在C语言中是如何表示的?
1、整数
十进制:传统的写法
十六进制:前面加0X或0x(数字0不是o)
八进制:前面加o(字母o不是0)
2、浮点数
传统写法 float x=3.2;
科学计数法 double x=1.2345e-2;
3、字符
单个字符用单引号括起来;
‘A’ 表示字符A
字符串用双引号括起来;
“ABC” 表示字符串ABC
“A”也是可以的,表示:’A‘ ’\0‘的组合
’AB‘写法错误,因为不是单个字符
二十四、常量以什么样的二进制代码存储在计算机当中?
计算机内部所有东西都是二进制存储的;整数是以补码的形式转化为二进制代码存储在计算机中;实数是以IEEE754标准转化为二进制代码存储在计算机中;字符的本质实际与整数的存储方式相同。
二十五、总结
课程知识回顾
二十六、代码规范化
推荐书:林锐《高质量C/C++编程》;有电子档
内部代码缩进;独立功能部分用换行隔开;等号的左右要加空格;
二十七、什么是字节
字节就是存储数据的基本单位,是硬件所能访问的最小单位;
计算机能最小的存储单位是:位;
CPU控制的最小单位是:字节;
CPU通过位运算符来控制;
1字节=8位;
1K=1024字节;
1M=1024K;
1G1024M;
1024=2的10次方;
二十八、不同数据之间相互赋值转换的问题
通过补码来转后,后期有专题;
二十九、char字符使用的常见问题
字符串不可以放给单个字符char;
char字符占一个字节;
’ ‘单引号代表单个字符;" "双引号代表字符串
字符串默认会在最后加:’\0’;所以单个字符不可放在双引号内赋值给char类型,否则报错;
变量可以多次赋值,但不可以多次重复定义;
举例:
int main(void)
{
char ch = ‘a’;//定义单个字符
//char sh = “AB”;//报错,char不可以用于存放字符串,char只占一个字节
//char th = ‘AB’;//报错,单引号内部只能存放一个字符的长度
//char xh = “C”;//报错,双引号存放字符串时,会自动在末尾加: ‘\0’ 所以不止一个字符长度,不可以存储
//char ch = “D”;//变量不可以重复定义
//sh = ‘E’;//
printf("%c\n",ch);
//printf("%c\n",sh);
//printf("%c\n",th);
//printf("%c\n",xh);
}
三十、什么是ASCII码
ASCII:不是一个值,而是一种规定,ASCII码规定不同的字符是使用哪个整数值去表示的问题;
例如:它规定A-65 B-66 0-48 a-97 b-98 等等;
还有不同的码:GB2312;UTF-8等等;
三十一、基本的输入输出函数的用法
printf()输出;
scanf()键盘输入;
三十二、建议
三十三、printf()用法–将变量的内容输出到显示器上
1、printf(“字符串\n”);
\n表示换行,是一个转义字符;字符串可以是中文或者英文;
举例:printf(“我是C程序\n”);
printf(“zxcvbnm\n”);
2、printf(“输出控制符”,输出参数);
在将数据定义后赋值给变量时,其实是将数据的二进制代码存储在了变量中,所以在数据输出的时候要说明输出的数据的类型,用输出控制符确定,即存储在变量内部的二进制该如何显示。
x或X–16进制 o–8进制 d–10进制;
举例:
int main(void)
{
int i = 10;//内部存放的是二进制码
printf("%d\n",i); //十进制 10
printf("%x\n",i); //小写十六进制 a
printf("%X\n",i); //大写十六进制 A
printf("%o\n",i); //八进制 12
}
3、printf(“输出控制符1 输出控制符2…”,数据参数1 ,输出参数2,输出参数3…);
输出控制符和输出参数的个数必须要保持一致;
举例:
int main(void)
{
int i = 10;
int j = 11;
printf("%d %d\n",i,j); //十进制输出 输出结果 10 11
printf("%d\n",i,j); //十进制输出 输出结果 10 输出控制符和输出参数个数不匹配
}
4、printf(“输出控制符 非输出控制符”,输出参数);
输出控制符一般用%开头;通常有:
%d ------ int
%ld ------ long
%c ------ char
%f ------ float
%lf ------ double
%x(或%X或%#X或%#x) ------ int或short或long
%o ------ int或short或long
%s ------ 字符串
%#X, %#x:给16进制添加0x或0X前缀,易于区分;
举例:
int main(void)
{
int x = 47;//x的初始值的十进制
printf("%x\n",x); //十六进制输出 输出结果 2f
printf("%X\n",x); //十六进制输出 输出结果 2F
printf("%#x\n",x); //十六进制输出 输出结果 0x2f
printf("%#X\n",x); //十六进制输出 输出结果 0X2F
}
推荐使用:%#X
三十四、保存有价值的代码
不同软件的运行结果不一样;
格式:
三十五、printf()用法讲解2
双引号内的数据在printf中是原样输出的。
printf(“输出控制符 非输出控制符”,输出参数);
输出控制符控制输出,用%开头;非输出控制符原样输出;
举例:
#include
int main(void)
{
int i=1;
int j=2;
printf("i = %d j = %d",i,j);//输出结果: i = 1 j = 2
}
三十六、为什么需要输出控制符?
注意:数据是以二进制的形式存放在我们定义的变量当中。
1、二进制01组成的代码可以表示数据也可以表示指令;所以必须存在输出控制符控制输出的格式。
2、如果二进制01组成的代码表示的是数据的话,那么同样的01代码组合以不同的输出格式输出就会存在不同的输出结果,因此需要输出控制符来控制具体的输出格式。
三十七、回顾总结
1、代码规范化:提高代码可读性;降低程序的出错率;
2、存储的基本单位是字节;计算机的最小单位是位;
3、ASCII是一种规范;
4、字符的存储方式实质上与整数的存储方式相同;
5、printf有四种用法;多种输出控制符;
6、为什么有输出控制符
三十八、scanf()用法
推荐用书:王爽 《汇编语言》
scanf()的用法:通过键盘将数据输入到变量中;
两种用法:
1、scanf(“输入控制符”,输入参数);
功能:将从键盘输入的字符转化为输入控制符所规定格式的数据;然后存入以输入参数的值为地址的变量中;
举例:
#include
int main(void)
{
int i;
scanf("%d",&i);//%d是控制输入符,用于将输入的数据转换为指定的格式
//&i表示将用户输入的数据放到i所对应的地址中,&就是一个取地址符
printf(“i = %d\n”,i);//用户输入:123 输出结果是:i = 123
}
三十九、scanf()用法2
2、scanf(“非输入控制符 输入控制符”,输入参数);
非输入控制符并不是必须存在的;在输入参数的时候必须要按照scanf的格式来输入,有非输入控制符存在时,必须要先输入非输入控制符;
非输入控制符必须原样输入;
举例:
#include
int main(void)
{
int i;
scanf(“m%d”,&i);//m是非输入控制符;%d是输入控制符,所以必须要先输入m再接数据
//&i表示将用户输入的数据放到i所对应的地址中,&就是一个取地址符
printf(“i = %d\n”,i);//输出结果是:i = 123
}
/*
正确格式:
m123
i = 123
错误格式:非输入控制符必不可少
123
i = 0
错误格式:C语言会自动提取正确的部分,除去错误部分
m123n
i = 123
错误格式:开头字母只能是指定的非输入控制符
n123
i = 0
*/
四十、scanf()用法3
scanf()也可以控制多个字符的输入;
在输入的不同值之间需要有回车或者空格隔开;
由于非输入控制符需要原样输入,如果输入控制符中间隔的是具体的符号,则需要原样的输入才可以进行正确的赋值,否则赋值错误;
举例:
#include
int main(void)
{
// int i,j,k;
// scanf("%d %d %d",&i,&j,&k);//非输入控制符需要原样输入 ,中间用空格或回车隔开
// //&i表示将用户输入的数据放到i所对应的地址中,&就是一个取地址符
// printf(“i = %d j = %d d = %d\n”,i,j,k);
// int x,y,z;
// scanf("%d,%d,%d",&x,&y,&z);//非输入控制符需要原样输入 ,中间用逗号隔开
// //&i表示将用户输入的数据放到i所对应的地址中,&就是一个取地址符
// printf(“i = %d j = %d d = %d\n”,x,y,z);
int o,p,q;
scanf("%dm%dm%d",&o,&p,&q);//非输入控制符需要原样输入 ,中间用m隔开
//&i表示将用户输入的数据放到i所对应的地址中,&就是一个取地址符
printf("i = %d j = %d d = %d\n",o,p,q);
}
/*
正确格式:中间间隔的是 回车
1
2
3
i = 1 j = 2 d = 3
正确格式:中间间隔的是 空格
1 2 3
i = 1 j = 2 d = 3
正确格式:中间间隔的是 逗号
1,2,3
i = 1 j = 1 d = 0
正确格式:中间间隔的是 m
1m2m3
i = 1 j = 2 d = 3
*/
四十一、如何scanf()的高品质代码
1、提供提示信息,提示用户输入的规则;
举例:
#include
int main(void)
{
int i,j,k;
printf(“请输入三个数,中间用空格隔开:”);
scanf("%d %d %d",&i,&j,&k);//非输入控制符需要原样输入 ,中间用空格或回车隔开
//&i表示将用户输入的数据放到i所对应的地址中,&就是一个取地址符
printf(“i = %d j = %d k = %d\n”,i,j,k);
// 运行结果:
// 请输入三个数,中间用空格隔开:1 2 3
//i = 1 j = 2 k = 3
}
2、当用户输入数据时只输入的前面满足输入的要求,则只读取正确的部分,不符合条件的部分将被保留下来,下一次需要数据输入的时候继续读取,这样的话,当用户输入时误操作了就可能曹诚后面的数据无法正确输入,所以在每个输入语句完成之后需要将输入的内容清空,让用户重新输入的新内容被直接读取;
举例:
#include
int main(void)
{
int i,j;
char ch;
scanf("%d",&i);//非输入控制符需要原样输入 ,中间用空格或回车隔开
printf("i = %d\n",i);
while((ch=getchar()) != '\n')//清空残留数据
continue;
scanf("%d",&j);//非输入控制符需要原样输入 ,中间用空格或回车隔开
printf("j = %d",j);
}
/*
错误输出:前面的123是正确的i的输入格式,所以程序正确的读写,但是残留的asdfgh会在j输入时格式错误,所以j无法正确赋值
123asdfgh
i = 123
j = 1
正确输入:i的输入值满足条件的情况下j的赋值可以正确进行
12
i = 12
34
j = 34
while语句解决:清空上一次输入残留的数据
123m
i = 123
456
j = 456
*/
while语句清空残留的数据:
四十二、运算符
1、算术运算符 +、-、*、/、%(取余)
2、关系运算符 >、<、>=、<=、!=、==
3、逻辑与算符 !(非)、&&(与)、||(或)
4、赋值运算符 =、+=、/=、-=
5、优先级
算数>关系>逻辑>赋值
四十三、除法和取余运算
四十四、逻辑运算符
1、C语言中 1表示真;0表示假;
&&:两者为真才为真;
||:两者为假才为假;
举例:
#include
int main(void)
{
int i = 2;
int m,n,k,j;
m = (3>2) && (i=3);//逻辑与运算符,第一个判断为真;第二个是赋值运算符,判断相等用:==
printf("m=%d i=%d\n",m,i);//输出结果m=1 i=3
n = (3>2) && (i==1);//i的值不为1 ,必须要两者同时满足条件
printf("n=%d i=%d\n",n,i);//输出结果n=0 i=3
k = (3>2) || (i>5);//满足一个条件即可
printf("k=%d i=%d\n",k,i);//输出结果k=1 i=3
j = (1>2) || (i>5);//两个条件都不满足
printf("j=%d i=%d\n",j,i);//输出结果k=0 i=3
}
2、短路现象
&&(和)当前半部分不满足,后面的条件不需要再执行,直接得出false(0)的结果;
||(或)当前半部分为真时,后面的条件直接不执行,直接得出为true(1)的结果;
举例:
#include
int main(void)
{
int i = 2;
int m,k;
m = (5<2) && (i=3);//与运算符,第一个判断为假后,直接判定为true,第二个赋值运算不会执行
printf("m=%d i=%d\n",m,i);//输出结果m=0 i=2
k = (3>2) || (i=5);//或运算符,满足一个条件即可 ,直接判断为false,第二个赋值运算不会执行
printf("k=%d i=%d\n",k,i);//输出结果k=1 i=2
}
四十五、运算符
在括号内部的运算先进行,先运算括号内的;
四十六、scanf()复习
1、非输入控制符的用途在于:将用户从键盘输入的数据以非输入控制符的形式放入计算机中;
&:将输入数据存到相应的变量的位置;
注意:用户多输入或者误输入的数据不会被C语言程序自动清除;当存在多个输入代码时,前面输入的数据错误会影响后面输入程序读取输入的数据;
例如:当第一个scanf()要求输入的是十进制(%d);第二个scanf()要求输入的是字符(%c);第三个scanf()要求输入的是十进制(%d);
如果用户在第一个scanf()中输入的是:123asd;
那么第一个scanf()开始读取,123满足十进制的要求,所以第一个输入赋值的是123,但是剩下的字符asd继续保存,没有清空,等待下一次读取;
那么第二个scanf()从上次读取的剩余部分开始读取,但是char字符的长度只能存储一个字符,所以只能读取赋值a一个字符,sd只能被继续保留,留作下次读取;
那么第三个scanf()从上次读取的剩余部分继续读取,但是要求的是十进制的格式,剩余部分为sd不是十进制,所以赋值失败;
补充:每一次scanf()要求输入数据时,输入的数据会接在以前保留在剩余数据的后面;
为了避免这种情况,一般我们会用while语句来清除输入的缓存数据;
举例:
#include
int main(void)
{
int i,j;
char ch;
scanf("%d", &i);
printf("i = %d\n", i);
scanf("%c", &ch);
printf("ch = %c\n", ch);
scanf("%d", &j);
printf("j = %d", j);
return 0;
}
/*
只有一次输入:
输入一次,赋值格式正确:
输入:123a5
i = 123
ch = a
j = 5
输入一次,赋值格式错误:
输入:123asd
i = 123
ch = a
j = 1 //赋值错误,不符合十进制格式
*/
2、注意:scanf()中不能在非控制输入符中使用\n,因为非空值输入符需要原样输入;
举例:
#include
int main(void)
{
int i;
scanf("%d\n", &i);//\n需要原样输入
printf("i = %d\n", i);
}
/*
结果:
输入:123
\n //原样输入\n才可完成值的输入和赋值
输出:i = 123
*/
四十七、什么是流程控制
程序代码执行的顺序;代码是从上往下的执行,但是不一定是每一条程序语句都需要执行,也不一定从最上面的第一条语句开始执行。
四十八、流程控制的分类
1、顺序执行
一条一条的按顺序每条都执行
2、选择执行
根据不同的条件执行不同的语句
3、循环执行
重复的执行某一部分的语句
四十九、总结回顾
1、scanf()的用法回顾
中间空格隔开;非空值输入符原样输入
2、除数的 / 两边的整数和小数求出的结果不一致;
两者都为整数,则除数结果为整数;
两者只要存在小数,则除数结果为小数;
3、求余%,符号两边必须是整数用来求余,不可以小数求余;
4、关系运算符
=表示赋值;
==表示判断是否相等;
a+=5即表示a=a+5;
a*=5即表示a=a*5;
5、逻辑运算符
&&两个全为真,则为真;
||只要有一个是真,则为真;
6、短路情况
五十、流程控制
流程控制是学习C语言的第一个重点;
程序控制就是程序代码执行的顺序;
分类:顺序、选择、循环;
看懂程序执行顺序、看懂代码;
五十一、选择结构
1、定义:某些代码可能执行,也可能不执行,有选择的去执行某一些代码;
2、分类:
if语句:if单分支选择语句;
switch语句:多分支选择语句;
五十二、选择结构-------if最简单的用法
1、格式:
if(表达式){
执行语句;
}
2、功能:
条件成立,执行语句;条件不成立,则不执行语句;
3、举例:
#include
int main(void)
{
if(0)//false—0;true----1
{
printf(“aaa\n”);//不会输出
}
if(1)
{
printf(“bbb\n”);//正常输出
}
if(2)
{
printf(“ccc\n”);//正常输出
}
if(3)
{
printf(“ddd\n”);//正常输出
}
}
五十三、选择结构-------if的范围问题
1、if语句内部如果只有一条执行语句,那么可以省略"{}"符号;多余一条语句就不能省略;
2、举例:
#include
int main(void)
{
if(3<2)
printf(“aaa\n”);
printf(“bbb\n”);
//只会输出bbb,if不满足,内部的代码不执行,但是第二条printf语句不受if语句的控制,会执行
if(5<2)
{
printf(“ccc\n”);//不会输出
printf(“ddd\n”);//不会输出
}
}
3、总结
五十四、选择结构-------if…else…用法
1、判断用户输入的两个值的大小
#include
int main(void)
{
int i,j;
printf("请放入需要比较的两个数,中间用空格隔开,回车结束:");
scanf("%d %d",&i,&j);
if(i>j)
{
printf("i的值比j的值大");
}
else{
printf("i的值比j的值小");
}
}
五十五、选择结构-------if…else if…else的用法
1、else if中间可以嵌套多个,格式如下:
if(条件)
{执行语句}
else if(条件)
{执行语句}
else if(条件)
{执行语句}
else
{执行语句}
2、求根公式
#include
int main(void)
{
int a,b,c;
float delta;
printf("请放入需要一元二次方程的三个系数,中间用空格隔开,回车结束:");
scanf("%d %d %f",&a,&b,&c);
delta=(b*b)-4*a*c;
if(delta>0)
{
printf("两个解");
}
else if(delta==0)
{
printf("唯一解");
}
else{
printf("无解");
}
}
3、每个条件后不加{}则默认只会执行一个语句;
五十六、选择结构-------C语言对真假的处理
非零则为真----true—1 表示
零就是假----false-----0 表示
五十七、选择结构-------if举例:求分数的等级
1、判断时不可以按照数学的逻辑来写;
例如:1<=i<=100
这样写的结果:无论i的值为多少,等式永远成立
原因:在C语言中程序的判断是存在优先级的,并且if语句判断后的结果是0或1即true或者false;按照上面的判断式,会先判断1是否小于i;若小于i那么前半部分变为0(true);若大于i则前半部分变为1(false);前半部分判断完毕之后,再拿产生的0或1的结果来与100比较,无论如何等式都成立。
代码:
#include
int main(void)
{
int i=1000;
if(1<=i<=100)
printf(“aa”);
}
注意:无论i的值取多少,aa永远都会正常输出;
2、分数等级判断
#include
int main(void)
{
float score;//定义保存用户输入的数据
printf("请输入您的分数,回车结束:");
scanf("%f",&score);
if(score>100)
{
printf("输入的分数不能大于100哦");
}
else if(90<=score && score<=100)
{
printf("优秀");
}
else if(70<=score && score<90)
{
printf("良好");
}
else if(60<=score && score<70)
{
printf("及格");
}
else if(0<=score && score<60)
{
printf("加油");
}
else{
printf("输入的分数不可以为负数哦");
}
}
五十八、选择结构-------if例子:互换两个数字
举例:
#include
int main(void)
{
int i,j;
int t;//临时变量
printf("请输入需要交换的两个数,回车结束:");
scanf("%d %d",&i,&j);
printf("交换前:%d %d\n",i,j);
t=i;
i=j;
j=t;
printf("交换后:%d %d\n",i,j);
}
五十九、选择结构-------对任意三个数字进行排序–求出最大值
代码:
#include
int main(void)
{
int a,b,c;
int t;
printf("请输入需要排序的三个数,回车结束:");
scanf("%d %d %d",&a,&b,&c);
//a是最大值 b是中间值 c是最小值 ;小的往后面换;大的往前面换
if(a {
t=a;
a=b;
b=t;
}
if(a
t=a;
a=c;
c=t;
}
if(b
t=b;
b=c;
c=t;
}
printf("a: %d b: %d c: %d",a,b,c);
}
六十、如何看懂一个程序
1、流程
2、每个语句的功能
3、试数
六十一、复习
1、复习:排序、互换数字;
六十二、选择结构-------if的常见错误-空语句问题
1、分号代表语句的结束;
2、if条件后面加“;”表示后面接一个空语句;
举例:
#include
int main(void)
{
if(3>2);//表示后面是一条空语句
printf(“aaa\n”);
printf(“bbb”);
//aaa和bbb都可以正常输出
}
注意:代码不会报错,接空语句并不是不可以的;
六十三、选择结构-------if常见问题
1、if…else…嵌套语句,在if后面加“;”接空语句时,else语句和if语句的结构被隔断,else不可以单独使用,所以程序报错;
2、if…else if… else if… else语句中,当第一条满足条件后,后面的语句将不会继续执行;
举例:
#include
int main(void)
{
if(3>2)
printf(“aaa\n”);
else if(3>1)//一旦第一条if语句满足条件后面的语句都不再执行,即使条件成立
printf(“bbb\n”);
else if(3>0)
printf(“ccc\n”);
else
printf(“ddd”);
//程序输出aaa
}
3、if…else if… else if… else最后的else语句可以省略,程序不会报错,但是逻辑上有漏洞;
4、格式:else(判断条件){执行语句}
该格式错误,else后不可以加条件,可以加条件的是if或者else if;
5、else后面也可以跟空语句;
#include
int main(void)
{
if(1>2)
printf(“aaa\n”);
else;//else后接的是一个空语句
printf(“ddd”);
//程序输出ddd
}
6、else后面接判断条件后再接“;”,程序会将判断条件当作是else的第一条语句,程序不会报错,但是逻辑存在错误,执行之后没有实际的输出结果;
#include
int main(void)
{
if(1>2)
printf(“aaa\n”);
else (3>1);//else后接的是(3>1)语句,实际只是进行了判断,没有实际的执行结果
printf(“ddd”);//不是if else结构内的语句
//程序输出ddd
}
六十四、switch后面再讲
六十五、循环结构-------定义
1、定义:某些代码会被重复执行
2、分类
3、break和continue
六十六、循环结构-------分类
三种结构:
1、for
2、while
3、do-while
六十七、循环结构-------为什么需要循环
1、需要循环的例子:从1累加到100 即 1+2+3+4+5+…+100的值
#include
int main(void)
{
int i;
int sum=0;//求和,必须要进行赋值为零,即进行初始化
for(i=1;i<=100;i++)
{
sum=sum+i;
}
printf("%d",sum);//输出结果:5050
}
注意:
1、必须要进行sum的初始化,因为对于没有进行初始化的数据,过去存储其他数据的垃圾值可能仍然存在,会对结果造成影响;
2、i是开始计算的初始值;i++是累加;
六十八、循环结构-------for循环最简单的用法
1、求1-10的奇数的和
#include
int main(void)
{
int i;
int sum=0;//求和,必须要进行赋值为零,即进行初始化
for(i=1;i<=10;i+=2)
{
sum=sum+i;
printf("本次累加结果:%d\n",sum);
}
printf("总和的结果: %d",sum);//输出结果:2500
}
六十九、循环结构-------for和if的嵌套使用
1、for运算符不加{}默认控制一条语句,多于一条语句应添加{}
举例:
#include
int main(void)
{
int i;
for(i=1;i<=5;i++)
{
printf("aaa\n");//输出5次
}
printf("bbb");//输出1次
}
2、求1-100之间所有能被3整除的数字之和
举例:
#include
int main(void)
{
int i;
int sum=0;
for(i=1;i<=100;i++)
{
if(i%3 == 0)
{
sum=sum+i;
printf(“当前的数:%d\n”,i);//输出当前的数
}
}
printf(“总和:%d”,sum);//输出求和总数 1683
}
3、无论if语句是否成立,只要for语句仍然在循环,printf语句每次都会被输出,因为if语句在不加{}时只能控制其后面的一条语句,此处if和printf语句没有关联,但if会改变sum的值,所以会对sum的值的大小有影响,而对是否正常输出没有影响;
i在for循环中的变化对于sum的变化:
作业1:求1到100之间的奇数之和
代码:
#include
int main(void)
{
int i;
int sum=0;
for(i=1;i<=100;i+=2)
{
sum=sum+i;
}
printf(“总和:%d”,sum);//输出求和总数 :2500
}
作业2:求1到100之间的奇数的个数
代码:
#include
int main(void)
{
int i;
int sum=0;
for(i=1;i<=100;i++)
{
if(i%2 != 0)//对2求余不为0即表示不为偶数
{
sum++;
}
}
printf(“总个数:%d”,sum);//输出求和总个数 :50
}
作业3:求1到100之间的奇数的平均值
代码:
#include
int main(void)
{
int i;
int sum=0;
int t=0;
float average=0;
for(i=1;i<=100;i++)
{
if(i%2 != 0)//对2求余不为0即表示是奇数
{
sum=sum+i;//将所有的奇数累加
t++;//记录奇数的个数
}
}
average=0.1*sum / t;
printf("奇数的总个数 :%d\n",t);//奇数的总个数
printf("奇数的总和 :%d\n",sum);//奇数的总和
printf("总平均数:%f\n",average);//输出求平均数 :50
}
作业4:求1到100之间的奇数之和 和 偶数之和 以及各自的平均数
代码:
#include
int main(void)
{
int i;
int sum1=0;
int sum2=0;
int t1=0;
int t2=0;
float average1=0;
float average2=0;
for(i=1;i<=100;i++)
{
if(i%2 != 0)//对2求余不为0即表示是奇数
{
sum1=sum1+i;//将所有的奇数累加
t1++;//记录奇数的个数
}
else{
sum2=sum2+i;//将所有的偶数累加
t2++;//记录偶数的个数
}
}
average1=1.0*sum1 / t1;//平均数是小数类型,但是除法需要除数和被除数任意一个为小数类型,才可以保证数据是小数类型,但是sum和t都是int类型,所有用0.1将sum转为小数类型再除
average2=1.0*sum2 / t2;
printf("奇数的总个数 :%d\n",t1);//奇数的总个数
printf("奇数的总和 :%d\n",sum1);//奇数的总和
printf("总平均数:%f\n",average1);//输出求平均数 :50
printf("偶数的总个数 :%d\n",t2);//偶数的总个数
printf("偶数的总和 :%d\n",sum2);//偶数的总和
printf("总平均数:%f\n",average2);//输出求平均数 :50
}
七十、循环结构-------复习
1、for用法复习
2、if的常见的错误
空语句问题;
if…else if…else…嵌套使用;
else后面可以接空语句但是不可以接条件;
七十一、循环结构-------强制类型转化的重要性
1、求1+1/2+1/3+…+1/100的和
代码:
#include
int main(void)
{
int i;
float sum=0;
for(i=1;i<=100;i++)
{
sum=sum + 1.0/i;//被除数或者除数必须有一个是小数才可以保证结果是小数;否则结果全为整数
}
printf("总和为: %f",sum);
}
2、强制类型转换
格式:(数据类型)(表达式)
功能:把表达式的值强制转换为前面所执行的数据类型
例如:(int)(4.2+2.5) 最终值为6
(float)(5) 最终值为5.000000
3、试数详细步骤举例
明确for循环的每个步骤,依次推理程序的程序的执行
七十二、循环结构-------浮点数存储所带来的问题
1、float和double都不能保证可以精确的存储一个小数;浮点数的存储方式不能精确存储小数,存储的实际上是近似值;
举例1:有一个浮点型变量x,如何判断x的值是否为0?
判断和差值是否非常的小来判断是否近似为0
举例2:为什么循环中更新的变量时不可以定义为浮点数?
因为浮点数的存储是近似值,并不是准确的值,我们定义的是1.0可能计算机内部存储的是0.999999
七十三、循环结构-------for循环和if的嵌套使用
课时六十九的四个课后习题:
1、作业1:求1到100之间的奇数之和
2、作业2:求1到100之间的奇数的个数
3、作业3:求1到100之间的奇数的平均值
注意:平均数是小数类型,但是除法需要除数和被除数任意一个为小数类型,才可以保证数据是小数类型,但是sum和t都是int类型,所有用0.1将sum转为小数类型再除
4、作业4:求1到100之间的奇数之和 和 偶数之和
七十四、循环结构-------多层for循环的嵌套使用
1、for循环的嵌套
当两个for循环进行嵌套时,外层for循环满足条件之后,执行内层for循环,知道内层for循环不满足条件之后,外层for循环进行第二次循环判断;
外层for循环每此循环的时候,内层的for循环外层了一次或多次循环(即直到不满足内部for循环时跳出);当外层for循环不满足条件时,执行语句B;
执行顺序:1->2满足条件->(内层for循环)4->5满足条件->A->6(自增运算)->5(满足条件)->A->6->5(不满足条件)->3->2(满足条件)->(内层for循环)4->5(满足条件)->A->6…
2、注意:该程序的结构总共两层:双层嵌套的for循环和B语句;
for循环在不加{}的情况下只能控制一条语句,外层for循环控制内层for循环;内层for循环控制A语句;B属于for循环外的语句;
3、语句的个数
外层for循环控制内层for循环;内层for循环控制多条语句;整体的语句个数为1
七十五、复习-进制
1、什么的是n进制?
逢n进1
2、把r进制转成十进制
例如:五进制234
转化为十进制:450+3*52+25^3
以此类推,指数从0开始依次增加,底数是被转化的进制的数,如果是六进制转十进制则以6为底,如果是八进制转十进制则以8为为底;
3、十进制转成r进制
十进制转二进制:
十进制转八进制:
十进制转十六进制:
注意:直接将需要转换的进制作为除数,十进制作为被除数,求余数,知道除的结果为0,所有余数构成的就是转换的结果;
4、不同进制所代表的数值之间的关系
本质上相同,只是表示的形式不同而已;
七十六、自增自减
1、运算符:算术运算符、关系运算符、逻辑运算符、赋值运算符
2、优先级别:算数》关系》逻辑》赋值
3、补充:自增、自减、三目运算符、逗号表达式
4、自增或自减运算符分类:
前自增 ++i
后自增 i++
5、前自增和后自增区别:
相同点:最终都是i的值+1;
不同点:
前自增:整体的表达式的值是i加1之后的值;即先自增后执行语句
后自增:整体的表达式的值是i加1之前的值;即先自增前执行语句
举例:
#include
int main(void)
{
int i,j,k,m;
i=j=3;//赋值运算
k=i++;//先赋值,在自增
m=++j;//先自增,再赋值
printf("i = %d j = %d k = %d m = %d",i,j,k,m);
//输出结果:i = 4 j = 4 k = 3 m = 4
}
6、为什么会出现自增?
i=i+1 等价于 i++;但是在内存中运算时不一样,i++直接放在寄存器中直接快速计算;
代码更加精炼;自增的速度更快;
7、学习自增自减要明白的几个问题?
例如:此代码的结构复杂,不易理解,可读性差,并且在不同的机器上运行的结果可能不同;
顺序点:“,”、“()”、“;”
8、自减和自增的用法一样
前自减 i-- 先运算,后自减
后自减 --i 先自减,后运算
七十七、运算符补充----三目运算符
格式:条件?语句1:语句2
相当于if…else…语句
if(条件)
{语句1;}
else
{语句2;}
条件成立执行语句1;不成立执行语句2
举例:
#include
int main(void)
{
if(3>2)
{
printf(“aaa”);
}
else
{
printf(“bbb”);
}
//运行结果:aaa
}
七十八、运算符补充----逗号表达式
格式:(A,B,C,D)
功能:从左到右执行,最终表达式的值是最后一项的值
举例:
#include
int main(void)
{
int i,j,k;
i=(1,2,3,4,5);//赋值给i的是最后一个数5
j=3;
k=(j++,++j,j+2,j-3);//执行代码分别是 k=4,k=5,k=5,k=2
//虽然赋值给k的j每次都在运算,但是j只是运算并没有赋值给j
//j+2 并不是 j=j+2 并不是赋值运算
//但是j++和++j是进行自增运算了的,如果是j+1的话j的值就不会改变,自增会改变j本身的值
printf("i = %d j = %d k = %d",i,j,k);
//输出结果:i = 5 j = 5 k = 2
}
七十九、复习-for循环的嵌套执行顺序
八十、多个for循环嵌套使用
举例1:
#include
int main(void)
{
int i,j,k;
k=0;
for(i=0;i<3;i++)
for(j=2;j<5;j++)
{
k++;
printf("%d\n",k);//一共输出9次,外层循环三次,内层循环3次
}
printf("aaaa");//输出一次
}
2、举例2
三个嘿嘿、三个哈哈,一个嘻嘻;两个单独的for循环和最后单独的一个printf语句
3、举例3
嘿嘿输出3次;哈哈和嘻嘻输出9次;呜呜输出3次
八十一、while循环--------概述
1、执行顺序
2、与for的相互比较
3、举例
4、什么时候使用while,什么时候使用for
八十二、while循环--------执行顺序
1、格式:
while(表达式)
{执行语句;}
2、在不加{}时,while循环默认执行其后面的一条语句;
八十三、for循环和while的差别
1、求1+2+3+…+100
while实现:
#include
int main(void)
{
int i;
int sum = 0;
i = 1;
while(i<=100)
{
sum=sum+i;
i++;
}
printf("%d",sum);//输出结果:5050
}
for实现:
#include
int main(void)
{
int i;
int sum = 0;
for(i=1;i<=100;i++)
{
sum=sum+i;
}
printf("%d",sum);//输出结果:5050
}
2、for循环和while循环是否可以转换?
3、for和while哪个比较好?
for的逻辑性更强,更不容易出错;while的初始值i定义在外部,不能重复使用;推荐使用 for循环;
八十四、如何看懂程序?
1、看懂程序需要看懂程序流程,理解程序逻辑;
2、判断用户输入的数是否是回文数,并返回结果
回文数:正着写和倒着写都一样,例如121、12321等都是回文数
程序实质:就是将用户的数据颠倒,再与原来的数据对比是否相等,若相等必为回文数
代码:
#include
int main(void)
{
int val;
int sum = 0;
int m;
printf("请输入需要判断的数据:");
scanf("%d",&val);
m = val;
while(m)//只要m不为0,判断的结果都为val
{
sum = sum*10+m%10;
m=m/10;//m为整型,结果会自动取整
}
if(sum == val)
{
printf("Yes\n");
}
else {
printf("No\n");
}
}
3、回文数程序判断解释
八十五、斐波拉契序列-1
1、斐波拉契序列:1 2 3 5 8 13 21 34 从第三项开始,后一项是前两项的和;
2、代码:f3是最终需要返回给用户的需要的值
#include
int main(void)
{
int f1=1,f2=2,f3;
int n,i;
printf("请输入你想知道的序列:");
scanf("%d",&n);
if(n == 1)
{
f3 = 1;
}
else if(n == 2)
{
f3 = 2;
}
else {
for(i=3;i<=n;i++)
{
f3=f1+f2;
f1=f2;
f2=f3;
}
}
printf("%d",f3);
}
3、代码循环解释
八十六、斐波拉契序列-2
八十七、什么时候使用for
for的逻辑更清晰;不同情况下使用,视情况而定;
八十八、do…while结构
主要是用于人机交互,无论条件是否满足,程序都会执行一次;
格式:
2、while和do…while的区别
while循环和for循环先进行条件判断,条件不满足时一次都不会执行;
while先执行代码语句,无论条件是否满足,程序都会执行一次;
do…while并不等价于for和while
八十九、do…while 实现一元二次方程
#include
#include
int main(void)
{
double a,b,c;
double delta;
double x1,x2;
int i;
do
{
printf("请输入一元二次方程的系数,回车符号结束输入:\n");
printf("a = ");
scanf("%lf",&a);//double类型用lf输出
printf("b = ");
scanf("%lf",&b);//double类型用lf输出
printf("c = ");
scanf("%lf",&c);//double类型用lf输出
delta = b*b-(4*a*c);
if(delta > 0)
{
x1 = ( -b + sqrt(delta) ) / 2*a;
x2 = ( -b - sqrt(delta) ) / 2*a;
printf("方程存在两个实根: x1 = %lf x2 = %lf\n",x1,x2);
}
else if(delta == 0)
{
x1 = x2 = -b / 2*a;
printf("方程存在一个单根: x1 = x2 = %lf\n",x1,x2);
}
else
{
printf("方程没有实根\n");
}
printf("是否需要继续,是请输入1: ");
scanf("%d", &i);//前面必须要加空格
} while(i == 1);
}
九十、switch的用法简介
1、楼层选择
#include
int main(void)
{
int val;
printf("请输入你要到的楼层:");
scanf("%d",&val);
switch(val)
{
case 1:
printf("一层开\n");
break;
case 2:
printf("二层开\n");
break;
case 3:
printf("三层开\n");
break;
default:
printf("没有盖到这一层\n");
break;
}
}
2、break必不可少,break用于结束语句,每一个case都是程序的入口,当程序满足某一个case的条件之后,从当前case开始,所有的语句都要执行,只有当遇到break时,才能使得跳出switch语句。
九十一、switch琐碎知识
1、注意当找到对应的case之后,程序按顺序执行,即使default再中间也会从default开始执行到最后
2、switch的代码错误举例示范:
#include
int main(void)
{
int val;
printf("请输入你要到的楼层:");
scanf("%d",&val);
switch(val)
{
case 1:
printf("一层开\n");
//break;
default:
printf("没有盖到这一层\n");
//break;
case 2:
printf("二层开\n");
// break;
case 3:
printf("三层开\n");
//break;
}
}
/*
当用户输入1时: 从case1开始执行到最后
请输入你要到的楼层:1
一层开
没有盖到这一层
二层开
三层开
当用户输入2时:从case 2开始执行
请输入你要到的楼层:2
二层开
三层开
当用户输入3时:从最后一个case开始执行
请输入你要到的楼层:3
三层开
当用户输入不在case范围内的数据时:从default开始执行
请输入你要到的楼层:4
没有盖到这一层
二层开
三层开
*/
九十二、break的用法
1、break如果用于循环时,则用于终止循环;
2、break用于switch语句时,用于终止switch语句;
3、break不能直接用于if语句,除非if语句是属于循环的一个字句
break终止的是for循环和switch语句,并不是if语句
代码:
#include
int main(void)
{
int i;
for(i=1;i<=5;i++)
{
if(3<2)
break;
printf(“hahahhaha\n”);
}
}
/*
当if的条件为假时输出结果:
hahahhaha
hahahhaha
hahahhaha
hahahhaha
hahahhaha
注意:if再不加{}时只能控制if语句后面的第一条语句
当if的条件为真时不会输出任何数据,break语句终止了循环
*/
4、如果break是用于多个for循环,break只能终止距离他最近的循环
#include
int main(void)
{
int i,j;
for(i=1;i<=5;i++)
{
for(j=1;j<=2;j++)
{
if(3>2)
break;
printf(“hahahhaha\n”);
}
printf(“lalalalalala\n”);
}
}
/*
当if的条件为假时输出结果:break没有终止内层for循环
hahahhaha
hahahhaha
lalalalalala
hahahhaha
hahahhaha
lalalalalala
hahahhaha
hahahhaha
lalalalalala
hahahhaha
hahahhaha
lalalalalala
hahahhaha
hahahhaha
lalalalalala
注意:if再不加{}时只能控制if语句后面的第一条语句
当if的条件为真时:break终止了内层for循环
lalalalalala
lalalalalala
lalalalalala
lalalalalala
lalalalalala
*/
5、再多层switch嵌套中,break只能终止离他近的switch语句
#include
int main(void)
{
int a=1;
int b=2;
switch(a)
{
case 1:
switch(b)
{
case 1:
printf("aaa\n");
break;
case 2:
printf("bbb\n");
break;//终止内层switch
}
printf("111\n");
break;//终止外层switch
case 2:
printf("222\n");
break;
default:
printf("333\n");
}
}
/*
输出结果:
bbb
111
*/
九十三、continue的用法
用于跳过本次循环余下的语句,转去判断是否需要执行下次循环;
for语句,while语句中同理
举例:
#include
int main(void)
{
int i;
for(i=1;i<=3;i++)
{
printf("111\n");//输出三次
continue;//终止了当前本次for循环,进行下一次for循环
printf("222\n");//一次都不输出
}
}
九十四、C语言与其他课程的东西
九十五、流程控制
顺序
选择
循环
九十六、测试举例
1、break和continue
continue和内层break终止的是for循环,continue执行之后,之后的语句不执行,执行j自增(即第二次foe循环),当执行到内层break时,for循环被直接终止,执行while的外层break,跳出while循环;
外层break语句用于跳出while循环,无论如何++i代码始终不会执行;
2、while语句的流程
3、continue是终止的是循环,而不是while语句;continue执行之后回跳到11行执行
4、continue和break都是对于循环起作用,不会对if起作用,如果放在if语句中,if决定它是否执行;
九十七、数组------概述
1、为什么需要数组?
2、数组的分类
一维数组
二维数组
多维数组
九十八、数组------数组的简单使用
1、什么是数组?
代码:
#include
int main(void)
{
int a[5] = {1,2,3,4,5};
int i;
for(i=0;i<5;i++)
{
printf("%d ",a[i]);//输出1 2 3 4 5
}
}
九十九、数组------为什么需要数组
1、可以保存和使用大量的同类型的数据;
2、数据可以模拟现实世界
一百、数组------数组不是学习重点
1、数组的值是固定的写入的值
2、数组的功能很少,需要我们自己写代码操作
3、数组存在可以操作的工具
一百零一、数组------一维数组的使用
1、一维数组:为n个变量连续分配存储空间,所有变量的数据类型必须相同;所有变量所占的字节大小必须相等;
2、数组初始化
初始化:
完全初始化:int a[5]={1,2,3,4,5};
不完全初始化,未被初始化的元素自动为0: int a[5]={1,2,3};
不初始化,所有的元素都是垃圾值:int a[5];
清零,内部所有的数据都是0:int a[5]={0};
错误写法:
int a[5];
a[5]={1,2,3,4,5};//此时是将后面的数据赋值个a[5],即下标为5的元素(第六个元素)
只有在定义数组的同时才可以进行整体赋值,其他情况下,整体赋值都是错误的
int a[5]={1,2,3,4,5}
a[5]=100;
此类写法也是错误的,数组中一共只有5个元素,下标为5表示的是第6个元素,该元素不存在
不可以直接写b=a;改写法默认的是将a数组的第一个元素的地址赋值给b数组的第一个元素,要用for循环依次每个元素的赋值
3、数组赋值
代码:
#include
int main(void)
{
int a[5];
int i;
printf("请输入5个数组数据:\n");
scanf("%d %d %d %d %d",&a[0],&a[1],&a[2],&a[3],&a[4]);
printf("数组数据:\n");
for(i=0;i<5;i++)
{
printf("%d ",a[i]);
}
}
3、数组数据倒置
代码:
#include
int main(void)
{
int a[7]={1,2,3,4,5,6,7};
int i = 0;
int j = 6;
int k,t,x;
printf("倒置前的数组数据:\n");
for(x=0;x<7;x++)
{
printf("%d ",a[x]);
}
printf("\n");
if(i
}
4、一维数组名不代表数组元素,而是代表一维数组第一个元素的地址
一百零二、数组------二维数组
1、元素a[行][列],确定了具体的数组的坐标位置,下标都是从 0开始的;
2、二维数组初始化
3、输出二维数组内容
需要两个for循环输出;行列依次输出
代码:
#include
int main(void)
{
int a[3][4]={
{1,2,3,4},//第一行元素
{5,6,7,8},
{9,10,11,12},
};
int i ;
int j ;
for(i=0;i<3;i++)//控制行
{
for(j=0;j<4;j++)//控制列
{
printf("%-5d",a[i][j] );//控制输出
}
printf("\n");
}
}
4、-5d用于对齐,负号表示左对齐,5表示中间的间隔是5个字符;
一百零三、数组------多维数组
1、是否存在多维数组?
不存在,因为内存是线性一位的;n维数组可以当作每个元素是n-1维数组的一维数组;
该数组是含有三个元素的一维数组,只不过每个元素可以再分为四个小元素
一百零四、函数------概述
1、函数是C语言的第二个重点
2、为什么需要函数?
3、什么叫做函数?
4、如何定义函数?
5、函数的分类?
6、注意的问题?
7、常用的系统函数?
8、专题:递归(数据结构中讲)
一百零五、函数------函数使用的简单介绍
程序优先执行main函数,void表示没有返回值;函数一般存在形式参数;避免写重复性的代码;
1、函数的第一个例子
#include
max(int x,int j)
{
if(x>j)
{
printf("%d\n “,x);
}
else{
printf(”%d\n ",j);
}
}
int main(void)
{
int a=2;
int b=1;
int c=7;
int d=9;
int e=0;
max(a,b);
max(c,d);
max(e,f);
}
输出结果:2 9 8
一百零六、函数------为什么需要函数
1、避免了重复性操作
2、有利于程序的模块化
一百零七、函数------什么是函数
逻辑上:能够完成特定功能的独立代码单元
物理上:能够接收数据(也可以不接收);能够对接收的数据进行处理;能够将数据处理的结果返回(也可以不返回)
总结:函数是一个工具,是为了解决大量类似问题而设计的,函数可以当作黑匣子;
举例代码:
#include
int change(void)//括号内的void表示不需要传入参数,int表示返回值的类型是整型
{
return 10;//函数的返回值
}
void get(int i)//传入的参数是整型的i;前面的void表示函数没有返回值
{
i = 0;
// return 20;//因为是无返回值的函数,所以return语句会报错
}
int main(void)
{
int j = 88;
int i = 99;
j = change();//保存change函数的返回值
get(i);
printf("%d\n",j); //输出10
printf("%d",i); //输出99
}
一百零八、函数------如何定义函数
格式:
函数返回值 函数的名字(函数的形式参数)
{
函数的执行体
}
1、函数定义的本质是:详细描述函数之所以能够实现某个特定功能的具体实现方法
2、格式:
return 表达式;
3、函数返回值的类型也成为函数的类型,因为如果函数名前的返回值类型和函数执行体中return返回值的类型不同的话,函数返回值的类型以函数前的返回值类型返回;
举例:
#include
int change(void)//主要是以函数名前面的类型来确定返回值的类型
{
return 10.56789;//函数的返回值
}
int main(void)
{
double j = 88.88;
j = change();//返回的本来是10,但是由于j是double类型,所以存在小数位
printf("%lf\n",j); //输出 10.000000
}
一百零九、函数------return和break的区别
1、break用于终止循环和switch语句
2、return用于终止被调函数,向主调函数返回表达式的值;
如果表达式为空,只终止函数,不返回任何值;
举例:
#include
int change(void)
{
printf(“aaaa\n”);//成功输出
return 10;//函数的返回值
printf(“bbbb\n”);//不会输出
printf(“cccc\n”);//不会输出
}
void get(void)
{
for(int i=0;i<=5;i++)
{
printf(“哈哈哈\n”);//输出一次
break;
printf(“嘎嘎嘎\n”);//不会输出
}
printf(“啦啦啦\n”);//输出一次
}
int main(void)
{
int j;
j = change();//调用change函数并接收其返回值
printf("%d\n\n\n",j); //输出 10.000000
get();//调用get函数
}
一百一十、函数------函数的分类
1、有参函数、无参函数
2、有返回值、无返回值
3、库函数、用户自定义
4、普通函数、主函数(main函数)
5、值传递函数、地址传递函数
一个程序有且必须只能有一个main函数,主函数可以调用普通函数,普通函数不能调用main函数
主函数的是程序的入口,也是程序的出口
普通函数可以互相调用
一百一十一、函数------举例
1、判断最大值-两种方式
#include
int change(int i,int j)
{
if(i>j)
return i;
else
return j;
}
void get(int i,int j)
{
if(i>j)
printf("%d",i);
else
printf("%d",j);
}
int main(void)
{
int j;
j = change(1,5);//调用change函数并接收其返回值
printf("%d\n\n\n",j); //输出5
get(6,10);//调用get函数 ,并输出 10
}
2、判断一个函数是否为素数(被1和本身整除)
思想:对1到本身中间的数只要存在可以整除的情况,则不是素数,for循环实现
代码:
#include
int main(void)
{
int val;
int i;
scanf("%d",&val);
for(i=2;i
if(val%i==0)
{
break;
}
}
if(i == val)
{
printf(“YES\n”);
}
else
{
printf(“NO\n”);
}
}
3、将判断素数的代码封装为函数
#include
bool Isprime(int val)
{
int i;
for( i=2;i
if(val%i==0)
{
break;
}
}
if(i == val)
{
return true;
}
else
{
return false;
}
}
int main(void)
{
int m;
printf(“请输入需要判断的数:”);
scanf("%d",&m);
if(Isprime(m))//函数的执行结果是布尔类型,if可以直接进行判断
{
printf(“YES\n”);
}
else{
printf(“NO\n”);
}
}
注意:所有的函数都是写在main函数的前面,在main函数中进行调用即可;
一百一十二、函数------复习上节课知识
1、return用于返回值;
2、void用于控制是否需要返回;
3、函数前面可以完全控制返回值的类型;
4、float和double都不能保障可以把所有的实数都准确的保存在计算机中
举例:
#include
int main(void)
{
float i=99.99;
printf("%f",i);//输出结果: 99.989998
}
5、break用于终止循环,return用于终止函数
一百一十三、函数------函数的声明
1、函数应该写在主函数的前面,原因?
因为程序是从上往下一条条代码的执行,并且main的程序的入口,如果将函数的调用执行到时,函数的定义部分却还没有执行,就会导致函数报错;
2、解决方法?
可以将函数写在main函数后面,需要在前面写函数的声明;且声明与原函数必须一致;遵循先定义后调用的原则;
3、函数声明的作用?
如果函数的调用卸载了函数定义的前面,则必须要加前置声明;
作用:
1)、告诉编译器即将可能出现的若干个字母代表的可能是一个函数;
2)、告诉编译器即将可能出现的若干个字母所代表的函数的返回值和形参的具体情况;
3)、函数声明是一个语句,末尾必须加分号,基本函数的声明已经放在了
#include <库函数所在的文件的名字.h>
语句中,不用再次声明;
代码:
#include
//void f(int);//声明错误,因为它与f函数不一致,f函数没有形参
int main(void)
{
f();//此处报错,因为没有声明表示函数没有定义就使用了
}
void f()
{
printf(“hahhaha”);
}
一百一十四、函数------形参和实参
1、形参和实参的个数、数据类型、位置必须相互对应相等
2、再数据类型方面,不一定完全相同,但至少可以相互转换,例如整型和浮点型都可以大致转换,但是字符串类型和整型无法大致转换,但是再java中对参数的传递要求严格,类型必须对应;
代码:
#include
void f(int x,float y)
{
printf(“hahhaha”);
}
int main(void)
{
f(6.6,7); //不会报错,整型和浮点型可以相互转换
f(“aaa”,5.5);//报错,字符串和整型无法转换
}
一百一十五、函数------如何再开发中合理的设计函数来解决实际问题
1、函数的功能尽量设计的单一,这样可以被下次重复利用,不要将功能和操作全部放在一个函数中,避免下次实现功能不同的时候又要重新去写相同部分的函数而只是执行的操作不同;
2、求1到某个数字之间所有的素数,并输出
代码(使用函数/或不使用):
方法一:不使用函数的情况(代码重复性高,利用率低)
#include
int main(void)
{
int val;
int i;
printf(“请输入您需要判断的范围:”);
scanf("%d",&val);
for(i = 2;i < val;i++)
{//单独取出该次循环中的所有值
int j;
for(j = 2;j < i;j++)//判断本次取出的值是否是素数
{
if(i%j == 0)//不是素数
{
break;
}
}
if(i == j)//最后取余到了本身
{
printf("%d\n",i);
}
}
}
方法二:(用函数判断是否为素数,,操作再main函数中执行,不放在函数中,函数功能单一)
#include
bool get(int m)
{//传入当前取出的数判断是否为素数,范围再main函数中进行循环
int j;
for(j = 2;j < m;j++)//判断本次取出的值是否是素数
{
if(m%j == 0)//不是素数
{
break;
}
}
if(m == j)//最后取余到了本身
{
return true;
}
else
{
return false;
}
}
int main(void)
{
int val;
int i;
printf(“请输入您需要判断的范围:”);
scanf("%d",&val);
for(i = 2;i < val;i++)
{//单独取出该次循环中的所有值
if(get(i))
{
printf("%d\n",i);
}
}
}
3、求素数的代码再次优化(全都封装为函数)
#include
bool get(int m)//本函数的功能是判断数据是否为素数
{
int j;
for(j = 2;j < m;j++)//判断本次取出的值是否是素数
{
if(m%j == 0)//不是素数
{
break;
}
}
if(m == j)//最后取余到了本身
{
return true;
}
else
{
return false;
}
}
void Isprime(int n)//本函数用于输出判断之后的素数
{
int i;
for(i = 2;i <= n;i++)
{//单独取出该次循环中的所有值
if( get(i) )//get函数需要先定义后使用,所以需要卸载Isprime函数的前面
{
printf("%d\n",i);
}
}
}
int main(void)
{
int val;
printf(“请输入您需要判断的范围:”);
scanf("%d",&val);
Isprime(val);
}
4、一个函数的功能要尽量独立单一;多学习,多模仿牛人的代码
一百一十六、函数------复习上节课知识
将不同功能的代码封装成多个功能单一的函数,提高代码的简洁性和利用率;
一百一十七、函数------常用系统函数和如何学习系统函数
1、函数是C语言的基本单位
2、类是java、c#、c++的基本单位
3、常用的系统函数(推荐用书:《Turboc2.0实用大全》 主要用于讲解函数)
sqrt 求平方根
abs 求绝对值
一百一十八、函数------递归
1、栈
类似于“一个杯子”,入口和出口相同,遵守先进后出原则
压栈:存入
出栈:调用
理解:A函数是如何调用A函数的?
栈是学习递归的基础,先学栈,再学递归
专题:栈?出栈?压栈?(自己找数据结构视频)
一百一十九、函数------函数复习
1、函数调用
#include
int f(int i)
{
i=99;
printf(“i= %d\n”,i);
return i;
}
int main(void)
{
int i=10;
printf("%d ",f(i));
return 0;
}
/*
程序输出结果:
i= 99
99
函数内外的定义不要重复
*/
2、函数内部使用变量是使用完毕之后立即释放空间;
一百二十、函数------变量的作用域和存储方式
1、按照变量的作用域分
全局变量:在所有的函数外部定义的变量,就是全局变量;
适用范围:从定义位置开始到整个程序结束;
局部变量:在一个函数的内部定义或者函数的形式参数,都是局部变量;
适用范围:只能在本函数内部使用;
注意:全局变量和局部变量的需要注意的区别?
在一个函数内部定义的局部变量和函数外部的全局变量名字重复时,局部变量会屏蔽掉全局变量;
#include
int k = 100;
void f(int k)//此处的int k是局部变量,与外部的全局变量同名,当在函数内部操作k时,默认将全局变量屏蔽
{
printf(“f函数:%d\n”,k);//输出结果是99
}
int main(void)
{
f(99);//传入参数赋值为局部变量k
printf("main函数:%d",k);//输出结果是100 k为全局变量
}
2、按照变量的存储方式分
静态变量
自动变量
寄存器变量(存放在内存中)
3、无论是函数还是变量,都需要先定义后使用
#include
void f(void)
{
printf(“f函数:%d\n”,k);//慈航报错,因为此处输出k时,k还没有被定义
}
int k = 100;//k是全局变量,使用范围是从定义开始到程序结束
int main(void)
{
f();
printf("main函数:%d",k);
}
一百二十一、指针------初次简单介绍
1、指针就是地址;内存是以字节为单位(8位2进制)
2、地址就是内存单元的编号;内存是划分为固定的空间的,并且每个空间有自己的固定编号
3、指针小程序
指针存放的是某一数据类型的地址,而变量存储的是数据,所有两者不可以相互赋值;
#include
int main(void)
{
int *p;//p用于存储int变量类型的数据的地址
int i = 3;
p = &i;//OK,&表示的是地址符号
// p = i;//error p用于存放地址,i用于存放变量
// p = 50;//error
}
4、p = &i;的具体意义到底是什么?
p保存了i的地址,因此p指向i;
p不是i,i也不是p;
修改 p 的值不影响 i 的值,修改 i 的值也不会影响 p 的值;
p是指针变量(可以存放其他变量的地址); i 是普通变量;
如果一个指针变量指向了某个普通变量,则 * 指针变量就完全等价于普通变量;
int * p只是指向,存放了它的地址;int 是数据类型,p是变量名字;
int * p不是表达定义了一个名字叫做p的变量
而是表示:p是变量名字,p变量的数据类型是int *类型,所谓int *类型实际上就是存放int变量 地址的类型;
例子:
如果p是个指针变量,并且p存放了普通变量i的地址,则p指向了普通变量i;*p就完全等同于i;
或者说:
在所有出现 *p 的地方都可以替换为 i
在所有出现 i 的地方都可以替换为 *p
图示:
5、指针小程序2
#include
int main(void)
{
int *p;//p用于存储int变量类型的数据的地址
int i = 3;
int j;
p = &i;//将i的内存地址赋值给p
j = *p;//当进行地址赋值之后,*p与i等价 等价于 j = i;
printf("i = %d\n j = %d",i,j);//i = 3 j = 3
}
6、*p是什么?
*p就是以p的内容为地址的变量;
p就是指定的某个类型的数据的地址变量;
p就是p指向的地址的内容;
7、指针和指针变量?
指针就是地址,地址就是指针;
地址就是内存单元的编号;
指针变量是存放地址的变量;
注意:编码和编号不一样,编码是规定某一个数据对于某一个数值,例如ASCII码规定a对应的是95;
指针和指针变量是两个不同的概念!
但是要注意:通常我们叙述时会把指针变量统称为指针,但是他们的实际不一样
自我理解:
指针就是地址,例如 当我们讲int类型的变量 i 的地址赋值给指针变量p时,
指针指的是 :变量i在内存中的16进制的地址;
指针变量指的是 :存放这个地址的变量,此处的变量是存储在p(类型是int * 存储地址的类型)里面的,所有p为指针变量;
一百二十二、指针------指针的重要性
1、可以表示一些复杂的数据结构(先学指针在学数据结构)
2、快速的传递数据
3、使函数返回一个以上的值(一般情况下只能return出一个数据,因为return语句会终止函数)
4、能够直接访问硬件(获取硬件信息)
5、能够方便的处理字符串
6、指针是理解面向对象语言中引用的基础(Java基础)
总结:指针是C语言的灵魂
7、指针的分类
基本类型指针
指针和数组
指针和数组
指针和结构体
多级指针
一百二十三、指针------什么是地址
1、到底什么是地址?
内存单元的编号,地址是从0开始的非负整数,地址的范围:4G(0到4G-1)(范围与计算机的内存大小有关系)
地址线:
32位的机器有232根地址总线,并且一次操作8位(单位:字节),所以一共控制2328位二进制;
内存条一次操作8位(一个字节),并且有0和1两种状态,一根线控制一种状态;
1K=2^10B
1M=210K=220B
1G=210M=220K=2^30B
计算机支持的空间大小和硬件和软件有关;
补充知识:
cpu可以直接访问内存,但是不可以直接访问硬盘,硬盘中的数据需要先调入内存中才能被cpu读取;
一百二十四、指针------什么是指针;
指针就是地址,地址就是指针;
指针变量就是存放内存单元编号的变量或者说指针变量就是存放地址的变量;
指针于指针变量类型不同,指针的本质就是一个操作受限的非负整数;
地址可以进行相减运算,不了可以进行加法,乘法、除法;
一百二十五、指针------基本类型的指针
一百二十六、指针------基本类型指针常见错误解析
1、变量在没有进行初始化的时候通常都是系统以前数据的垃圾值,对于指针变量也是一样的,如果只是定义而没有初始化指针变量,那么指针变量内部存储的都是垃圾值地址,*p代表的也是垃圾地址所指向的哪个数据;
2、虽然没有初始化的变量存放了垃圾数据的地址,但是也不可以随意改变这个垃圾地址所指向的数据的值,因为初始化就去操作某一个内存空间(类似于病毒攻击)对于系统来说是危险的;
#include
int main(void)
{
int *p;//局部变量
int i = 5;
// *p = i;//非法赋值,p是代表的是没有进行初始化的垃圾地址对应的值,不可以进行操作
//注意:此处的p和i的都是int类型,赋值不会出错
printf("%d",*p);
}
3、注意:*p代表的是地址对应的内部的值,是int类型;p代表的是地址(是指针变量)int *类型
#include
int main(void)
{
int * p;//局部变量
int * q;
int i = 5;
q = &i;//赋值正确 q是int *类型,应赋值变量的地址
*p = q;//赋值错误 *p是int类型 ,q是int *类型,并且*p中存储的垃圾地址内部的垃圾值,不可以随意更改 ;
*p = *q;//赋值正确,语法无错,类型一致,但是 *p中存储的垃圾地址内部的垃圾值,不可以随意更改 ,在任何时候都不可改变内部的地址指定的内存空间对应的值;
}
4、自我理解:
5、可以操作系统分配给变量的内存空间,但是没有分配的空间是不可以进行操作的
p空间内存已经分配,但*p空间并未分配,他只能代表所指向的地址内部的数据,但是不可以读取,改写,删除等等;
一百二十七、指针------互换两个数字
1、经典指针程序-----互换数字
解法一:多设置一个变量用于交换
错误代码:交换的是p,q内部的地址
#include
int main(void)
{
int a = 3;
int b = 5;
int t;
t = a;
a = b;
b = t;
printf(" a = %d\n b = %d",a,b);//输出结果:a = 5 b = 3
}
解法二:将交换的过程封装成函数?
不成功代码:
#include
void change(int a ,int b)//形式参数a,b是change函数内部的局部变量
{
int t;
t = a;
a = b;
b = t;
printf("change函数: a = %d b = %d\n",a,b);//输出结果:a = 5 b = 3
}
int main(void)
{
int a = 3;
int b = 5;//a,b是main函数内部的局部变量
change(a,b);
printf("main函数: a = %d b = %d",a,b);//输出结果:a = 3 b = 5
}
解法三:指针交换?
#include
void change(int * p ,int * q)//形式参数p,q是change函数内部的局部变量 ,获取的应该是int类型数据地址
{//p指向a,*p代表的是3;q指向b,*q代表的是5
int * t;
//t交换的是change函数内部的a,b的值,并不是main函数内部a,b
t = p;//将p和q内部所存放的地址进行交换
p = q;
q = t;
//p指向b,*p代表的是5;q指向a ,*q代表的是3
printf(“change函数: p = %d q = %d\n”,p,q);//输出结果:change函数: p = 6487576 q = 6487580
printf(“change函数: *p = %d *q = %d\n”,*p,*q);//输出结果:change函数: *p = 5 *q = 3
}
int main(void)
{
int a = 3;
int b = 5;//a,b是main函数内部的局部变量
change(&a,&b);//传入的是int *类型
printf("main函数: a = %d b = %d",a,b);//输出结果:a = 3 b = 5
}
4、正确解法:应该利用指针来改变其内部地址对应数据的值
#include
void change(int * p ,int * q)
{
int t;//注意此处交互的是int类型数据,不是数据地址,所以应该定义为int类型
//*p或者*q代表就是指针变量内部的地址所指向的值,即a和b的值,所以就直接改变了a和b的值
t = *p;//p是int * 类型; *p是int类型
*p = *q;
*q = t;
}
int main(void)
{
int a = 3;
int b = 5;//a,b是main函数内部的局部变量
change(&a,&b);//传入的是int *类型
printf("main函数: a = %d b = %d",a,b);//输出结果:main函数: a = 5 b = 3
}
5、注意:
一百二十八、指针------星号( * )的三个含义
1、乘法
2、定义指针变量
3、指针运算符(指针变量内部的地址指向的变量*p)
一百二十九、指针------复习
1、基本类型指针
2、单个字符的定义需要放在单引号(‘’)内部,字符串应该放在双引号内部(“”)
例如:char i = ‘A’;
char i = “asdf”;
代码举例:
#include
int main(void)
{
int * p;
int i = 3;
char k = ‘a’;
p = &i;
// p = &k;//报错,p只能存放int类型数据的地址
*p = 99; // 当p指向i时,*p代表的就是i
printf(“i = %d, *p = %d”,i,*p);//输出:i = 99, *p = 99
};
3、实参和形参不是同一个变量
main函数内部和我们自定义函数内部变量可以同名,但是更改的时候只能改变本函数内部的局部变量或者全局变量;
函数调用时给函数内部的局部变量分配内存空间,函数执行完毕之后要释放函数的空间,内部的局部变量空间也被释放;
#include
void f(int i)//f函数的局部变量 i
{
i = 88;
printf(“f: i = %d\n”,i);
}
int main(void)
{
int i = 99;//main函数的局部变量 i
printf(“main: i = %d\n”,i);
f(i);//f函数传入的参数是99,但是函数执行完之后,分配给f 函数局部变量i的空间被释放,并未改变main函数的i
printf(“main: i = %d\n”,i);
};
/*
输出结果:
main: i = 99
f: i = 88
main: i = 99
main函数内部的局部变量i和f函数内部的局部变量i相互独立
/
4、数字互换
利用指针来指向更改前的值,将指向的地址的值p和*q互换,即达到了数字互换;
一百三十、指针------通过指针为什么可以使被调函数返回一个以上的值
1、return用于结束函数,一个函数只有一个return会生效
2、如果不使用指针更改地址,形式参数永远不能改变实际参数的值(因为他们各自属于各自函数的局部变量)
3、如何通过被调函数修改主调函数的普通变量的值
1)、实参必须为该普通变量的地址
2)、形参必须是指针变量
3)、在被调函数中,通过修改 " *形参名 = …" 的方式,就可以修改主调函数相关变量的值(也可以修改多个值,但return只能返回一个值)
代码举例:
#include
int f(int i,int j)//f函数的局部变量 i
{
return 100;
return 80;//不会返回,只要函数执行到return就会终止函数
}
void g(int * p,int * q)
{
*p = 1;//代表修改的是p,q函数内部的地址所指向的值
*q = 2;
}
int main(void)
{
int a = 50;
int b = 20;
int k;
k = f(a,b);
printf("%d\n",k);
g(&a,&b);
printf("a = %d, b = %d",a,b);
};
/*
输出结果:
100
a = 1, b = 2
*/
一百三十一、指针------一维数组和指针关系概述
1、指针和一位数组
1)、数组名
数组是连续的
2)、指针和下标的关系
3)、指针变量的运算
2、指针和二维数组
3、数据的回顾
一百三十二、指针------下标和指针的关系
1、指针和一位数组
1)、数组名
一维数组名是一个指针常量;它存放的是一维数组第一个元素的地址;
代码:
#include
int main(void)
{
int array[5];
//数组类型:int 数组名称:array
printf("%#X\n",&array[0]);//数组中第一个元素的地址
printf("%#X\n",array); //数组名字指向的地址
}
/*
输出结果:
0X62FE00
0X62FE00
在未进行初始化时,数组的第一个元素的地址和数组名指向的地址相同
在计算机中,数据的地址一般以十六进制存储
*/
一百三十三、指针------一维数组名的含义
1、指针和一位数组
1)、数组名
一维数组名是一个指针常量;它存放的是一维数组第一个元素的地址;
代码:
#include
int main(void)
{
int array[5];
//数组类型:int 数组名称:array
printf("%#X\n",&array[0]);//数组中第一个元素的地址
printf("%#X\n",array); //数组名字指向的地址
}
/*
输出结果:
0X62FE00
0X62FE00
在未进行初始化时,数组的第一个元素的地址和数组名指向的地址相同
在计算机中,数据的地址一般以十六进制存储
*/
2、数组不可以通过数组名直接进行赋值,因为数组名表示的是数组第一个元素的地址
3、十六进制输出:%#X
一百三十四、指针------确定一个一维数组需要两个参数及其原因
1、确定一个一维数组需要几个参数(如果一个函数要处理一个一维数组,则需要接收该数组的那些信息)
2、数组名存放的是数组的第一个元素的地址,所以数组名是int *类型
3、需要传送给函数做参数的是第一个元素的地址和数组的长度
原因:有了数组的第一个元素就可以依次找出数组后面的元素,找到数组的起始点
数组的长度决定了在for循环中输出地址的时候的终止的地方,找到数组的终止点
4、当讲字符串作为实参传入时可以不用传入长度值,因为字符串是以“ \0 ”结束,可以判断出
5、练习—利用函数将数组中的元素输出
#include
void f(int * Arr,int len)
{//Arr代表的是地址
//Arr代表的就是当前数组地址对应的第一个元素的值
for(int i=0;i
printf("%d ",
}
printf("\n");
}
int main(void)
{
int array[5] = {0,1,2,3,4};
int array1[5] = {5,6,4,9,8};
int array2[7] = {9,10,11,12};
f(array,5);//数组名代表的是地址,所以是指针变量
f(array1,5);
f(array2,7);
}
/*
输出结果:
0 1 2 3 4
5 6 4 9 8
9 10 11 12 0 0 0
*/
6、利用指针改变数组中的元素的值
#include
void f(int * Arr,int len)
{
Arr[2] = 88;
for(int i=0;i<5;i++)
{
printf("%d ",Arr[i]);//输出结果:0 1 88 3 4
}
printf("\n");
}
int main(void)
{
int array[5] = {0,1,2,3,4};
printf("%d\n",array[2]);// 输出结果:2
f(array,5);
printf("%d\n",array[2]);// 输出结果:88
}
/*
输出结果:
2
0 1 88 3 4
88
*/
自我理解:
array[3]等价于*[array+3],数组名array本身就是一个指针,使得它指向不同位置的数了而已,array内部存放的是第一个数组元素的地址;
一维数组名是指针常量,不是指针变量!!!!!
一百三十五、指针------复习
1、数组名是指针常量,指向的是数组第一个元素的地址
2、数组名是指针常量,不是指针变量,所以不可以把地址赋值给它,它默认指向数组的第一个元素
3、指针和下标的关系,可以在自定义的函数的内部操作main函数中的数组的元素
4、操作数组需要传递的参数(数组名就指向数组第一个元素的地址,利用*p就可以操作元素)
5、指针可以直接操作数组
6、函数内利用for循环数组数组
只有通过传入地址可以在函数内部改变数组的值,否则在一个自定义的函数中无法改变main函数中数组的数据;
一百三十六、指针------指针变量的运算
指针变量不能做加法、乘法、除法;
指针变量可以做减法;
如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量才可以相减;
#include
int main(void)
{
int array[5] = {0,1,2,3,4};
int * p;
int * q;
p = &array[1];
q = &array[3];
printf("两者所指向的单元相差的空间距离为:%d\n",q-p);
// 输出结果:两者所指向的单元相差的空间距离为:2
}
一百三十七、指针------何谓变量的地址 一个指针变量到底占几个字节
预备知识:
sizeof(数据类型)
功能:返回值就是该数据类型所占的字节数
sizeof(变量名)
功能:返回值是该变量所占的字节数
1、一个指针变量到底占几个字节?
假设p指向char类型变量(1个字节)
假设q指向int类型变量(4个字节)
假设r指向double类型变量(8个字节)
p、q、r 本身所占的字节数是否一样?
是的,都是四个字节(32位的机器)(64字节的机器所占的是8个字节)
总结:一个指针变量,无论它指向的变量占几个字节,该指针变量本身只占四个字节;
一个变量的地址是用该变量首字节的地址来表示的;
(注意:2^10B=1KB 220B=210KB=1M 230B=220KB=2^10M=1G B即为字节)
位:bit 字节:byte 1byte=8bit
代码:
#include
int main(void)
{
char ch = ‘A’;
int i = 4;
double key = 6.666;
char * p;
int * q;
double * r;
p = &ch;
q = &i;
r = &key;
printf("%d %d %d",sizeof(p),sizeof(q),sizeof(r));
//输出结果:8 8 8
}
自我理解部分:
例如:p表示指向了i,p内部存储的是i开始存储的第一个字节的地址,int类型确定i是四位,*p即可代表指向的地址内部的内容;
一百三十八、指针------动态内存分配概述
1、传统数组的缺点
2、为什么需要动态内存分配
3、动态内存分配举例—动态数组的构造
4、静态内存和动态内存的比较
5、跨函数使用内存的问题
一百三十九、指针------传统数组的缺点【重点难点】
1、数组的长度必须事先制定,并且只能是常整数,不能是变量
2、传统形式定义的数组,该数组的内存程序员无法手动释放
在一个函数的运行期间,数组一旦定义,系统就会为该数组分配的存储空间就会一直存在,除非函数运行结束,数组的空间才会被系统释放;
3、数组的长度不可以在函数运行的过程中动态的扩充或者缩小(数组一开始就定义好了长度不能中途进行改变)
4、传统方式定义的数组不能跨函数使用
A函数定义的数组,在A函数运行期间可以被其他函数使用,但是当A函数运行完毕之后,A函数中的数组将无法继续被其他的函数使用(因为函数终止会释放函数占用的空间)
代码:
#include
void get(int * p,int len)
{
for(int i=0;i
printf("%d ",p[i]);//输出结果:1 2 3 4 5
}
}
void change(void)
{
int array[5]={1,2,3,4,5};//当函数执行完毕或者终止之后,get函数无法在使用change函数的数组
get(array,5);
}
int main(void)
{
change();
}
一百四十、指针------为什么需要动态分配内存【重点】
很好的解决了传统数组的四个缺陷;
传统数组也叫静态数组;
一百四十一、指针------malloc函数使用的简单介绍
1、malloc函数是memory allocate(内存分配)的缩写;
2、要使用malloc函数必须添加malloc.h这个头文件;
3、malloc函数只有一个形式参数,并且是整数类型;
4、malloc(4)中的4表示请求系统为本程序分配4个字节;
5、malloc函数会为我们分配内存,但只能返回分配地址的第一个字节的地址,根据我们转换的类型,来确定分配给我们的空间以什么类型来划分;
自我理解:
6、加malloc函数分配的内存都是动态内存,我们普通的定义变量都是静态内存分配
7、int * p = (int *)malloc(4);
该行代码系统一共给他分配了8个字节,在32位的计算机中,p占的字节数位4,p所指向的内存也占了4个字节,但实际上p只指向了都一个字节;
只有在malloc的括号中的数字才是系统动态分配的内存,而用类型名和变量名定义的都是静态分配的,所以此处的p(指针变量)是静态分配,p所指向的内存是动态分配的(4个字节)
8、动态内存分配和静态内存分配的大小不同,方式不同,运行速度也不同;
9、静态内存只有在程序运行完毕(或者函数运行完毕)或终止之后,才会被系统所释放(不可以手动释放),并且如果在函数内部定义的话,函数结束则定义的东西也不可以再用,而动态内存只有通过free(名称)释放之后,才不可以被使用;
代码:
#include
#include
int main(void)
{
int i = 5;//静态分配内存
int * p = (int *)malloc(4); //malloc只有一个形式参数, (int *)用于确定 系统动态分配的内存如何使用
*p = 4;//*p代表是整型变量 ,但是它和 i 的分配方式不同
free(p);//释放的是p指向的内存,p是静态内存,并且本身的内存只有在main函数借宿之后自动释放,不可以手动释放
printf("aaa");//依然执行
}
一百四十二、指针------malloc函数的用法续
1、sizeof(数据类型)表示返回值是对应的数据类型所占的字节数 ;
例如:sizeof(int)//表示返回值是int所占的字节数 ,返回的字节数为4
代码及理解:
#include
#include
int main(void)
{
int * p = (int *)malloc(sizeof(int)); //sizeof(int)表示返回值是int所占的字节数
*p = 10;//
}
2、改变动态地址内部存储的数据
#include
#include
void f(int * q)
{//p和q指向同一个变量,p和q代表的都是一样的
*q = 200;
}
void g(int * k)
{//p和q指向同一个变量,p和q代表的都是一样的
*k = 2;
}
int main(void)
{
int * p = (int *)malloc(sizeof(int)); //sizeof(int)表示返回值是int所占的字节数
*p = 4;//
printf("%d\n",*p);
f(p);
printf("%d\n",*p);
//free(p);//释放系统动态分配的四个字节
//free语句 释放了内存,虽然g函数还是正常的调用了,但是在此处不应该释放p所指向的内存
g(p);
printf("%d\n",*p);
}
/*
输出结果:
4
200
2
*/
一百四十三、指针------动态内存分配举例_动态一维数组的构造
1、传入数组的缺点
2、int array[3]={0,1,2}; 一共给该数组分配了12个字节大小的内存空间
3、动态构造一维数组
注意:指针变量可以指向不同长度的内存空间,但是其实它内部只保存了指向的内存空间的第一个字节的地址,由指针变量的类型决定指向的这个地址的长度,*p代表的也是指向的地址的第一个字节内部的数据;
4、由用户决定动态分配的内存的大小
代码:
#include
#include
int main(void)
{
int array[5];//分配的也是20个字节 静态构造
int len;
int * pArr;
printf("请输入需要存放的元素的个数:")
scanf("%d".&len);
pArr = (int *)malloc(4*len);//每个元素占4个字节,共需要存储5个元素,所以总共需要分配5*4=20个内存空间(字节),实质上也是在定义一个数组 动态构造 int表明数组中每个元素的类型
//等价于 int pArr[len];
}
理解:
pArr[0]=pArr pArr[1]=(pArr+1)
pArr是数组名,指向的是第一个数组元素的地址(于普通的数组相似),元素类型是int;
5、对动态一维数组进行赋值和输出
#include
#include
int main(void)
{
int array[5];//静态定义数组
int len;
int * pArr;
printf("请输入需要存放的元素的个数:");
scanf("%d",&len);
pArr = (int *)malloc(4*len);//每个元素占4个字节,共需要存储5个元素,所以总共需要分配5*4=20个内存空间
//等价于 int pArr[len]; 动态定义数组
for(int i=0;i
}
6、free用于释放动态分配的内存,并且程序继续执行,而静态数组不可手动释放,必须等到程序结束
free(pArr);
7、realloc(数组名,需要改变到的空间大小);函数用于扩充或缩小多分配的内存空间
例如:realloc(pArr,100);
表示把原来大小的内存空间改变为100个内存的大小(可能是缩小也可能是扩充)
如果是扩充:若原来分配的内存的大小是50字节,那么如果要扩充为100字节,原来的50字节将被保留,在后面多加上50个字节即可;
如果是缩小:若原来分配的内存的大小是150字节,那么如果要缩小为100字节,原来的150字节将被裁剪掉后面的50个字节,保留前面的100个字节;
8、在本次的例子中体现了动态内存随时释放,可以更改分配内存空间大小的有点,但是没有体现可以跨函数使用的优点;
9、动态数组的使用和静态数组的使用的相同的,没有差别,只是创造的方式不同而已;
一百四十四、指针------动态内存和静态内存的比较【重点】
1、静态内存是由系统自动分配的,由系统自动释放
2、 静态内存是在栈分配的
3、堆和栈;堆不是一种存储结构
4、函数的使用:
5、动态内存由程序员手动分配,手动释放;
动态内存是在堆中分配;
什么是堆?
堆就是堆排序,堆排序是排序的一种;
堆排序不放入栈中,可以跨函数使用;
一百四十五、指针------多级指针
1、什么是多级指针
代码:
#include
int main(void)
{
int i = 5;
int * p = &i;
int ** q = &p;
int ***r = &q;
printf("i = %d\n",***r); //输出结果:i = 5
}
自我理解:
2、多级指针传参
代码:
#include
void f(int ** q)
{
**q = 88;
//*q就是p。**q就是i,*q指向的是p的地址
}
int main(void)
{
int i = 5;
int * p = &i;
printf("i = %d\n",*p); //输出结果:i = 5
f(&p);
printf("i = %d\n",*p); //输出结果:i = 88
}
一百四十六、指针------复习上节课知识
1、p是指针变量,p是int *类型,&p是int **类型
2、int *p = (int *)malloc(4);系统一共分配了8个字节的内容,p本身是int *类型占4个字节,malloc函数又让系统动态分配了四个字节的内存;
3、静态内存是在栈内部进行分配,动态内存在堆里面进行分配;
一百四十七、指针------闲聊
一百四十八、指针_28_静态变量不能跨函数使用详解【重点】
1、跨函数使用内存
代码:
该程序的体现的是静态内存分配在函数执行完毕释放空间之后,不可以在使用释放掉的内存;
#include
void f(int ** q)
{
int i = 5;
*q = &i;//等价于p = &i
}
int main(void)
{
int *p;
f(&p);
printf("%d",p); //输出结果:5
}
/
程序逻辑错误:
f函数在使用完毕之后,f函数所占用的内存将会被全部释放掉,那么i是属于f函数的内部定义的变量,也随之释放
p是指针变量,定义在main函数中,可以对p进行调用和写入;
但是对于i在f函数执行完毕之后,i的空间已经被释放,所以,p是不可以使用的;
因为p等价于i,并且i是静态内存;
但是p可以继续使用;
*/
一百四十九、指针------动态内存可以跨函数使用详解【重点】
动态分配的内存是程序员手动分配,所以只有程序员手动释放之后才不可以使用;
代码:
#include
#include
void f(int ** q)
{
*q = (int *)malloc(sizeof(int)); //sizeof(数据类型)表示要求系统分配的是对应数据类型大小的内存空间
//等价于 p = (int *)malloc(sizeof(int)); *q就是p,p是指针变量,q执行p的地址
//malloc动态分配内存,必须用free释放之后才不可以使用,可以跨函数使用
**q = 88;//等价于 *p = 88;将动态分配的内存空间存入整型数据
//q代表p的地址,q代表p,**q代表p
}
int main(void)
{
int *p;
f(&p);
printf("%d",*p); //输出结果:88
}
malloc用于动态分配内存,*q存放的是系统分配的四个字节的空间的第一个字节的地址;
在32位的计算机中,无论指针变量是什么类型,指针变量都默认长度是四个字节;
p和q都是指针变量;
一百五十、指针------跨函数使用内存习题测试【重点】
计算机二级C语言—查漏补缺
1、要使得main函数中定义的指针变量在自定义的函数中进行合法的指向;
传入的是必须是指针变量的地址,因为改变的是指针变量所指向的地址;
自定义函数的形参必须是int **类型,因为传入的实参是int *类型;
利用自定义函数内部的 *指针变量名 来控制main函数中指针变量指向的地址;
注意:要想main函数中的指针变量在自定义函数执行完毕之后依然能够合法的指向它内部的地址,使用内部地址的数据内容,必须要采用动态内存分配的方式;
一百五十一、结构体1_为什么需要结构体 什么叫结构体【重点】
1、有助于学习Java中的类
2、为什么需要结构体?
复杂的数据无法用普通的基本数据类型来表示;
3、结构体的格式
4、利用结构体可以创造出新的数据类型,它可以由不同的数据类型组成,内部的数据不用多次的重复去定义;利用结构体创造好新的数据类型之后,可以在函数中去定义该种数据类型的变量;
目的:为了表示一些新的复杂的数据,我们将一些简单的数据类型组合在一起组成了一个新的数据类型,该部分单独成为了一个数据类型,就是结构体;
5、什么是结构体?
把一些基本类型的数组组合在一起形成的一个新的复合数据类型,这个就叫做结构体;
代码:
#include
struct Student//定义结构体,结构体是新的复合数据类型,类型名称为Student
{
int key;
double score;
char sex;
}
int main(void)
{
struct Student st = {88,88.88,'B'};//Student类型的变量,变量名为st
//以下的是我们普通的定义方法,在定义的时候遇到重复的会非常的麻烦
int key =1;
int key2 = 2;
char name;
char name2;
}
一百五十二、结构体2_如何定义结构体[3种方式]
只需要掌握第一种,其余两者不推荐使用,也不常用;
代码:
//第一种
struct Student//单独定义结构体,后期如果需要使用该类型,自行定义该类型变量使用即可
{
int key;
double score;
char sex;
};
//第二种
struct Student1//定义结构体的同时定义了该结构体的变量,所以该结构体只有st1这一个变量,不可再定义其他变量,或者重新定义结构体
{
int key;
double score;
char sex;
} st1;
//第三种
struct //可以不定义结构体的名字,将该类型的数据结构的变量名定义在后面
{
int key;
double score;
char sex;
}st2;
一百五十三、结构体3__怎样使用结构体变量概述
1、赋值和初始化
2、如何去除结构体变量种的一个成员
3、结构体变量的运算
4、结构体变量和结构体变量指针作为函数参数传递的问题
5、举例----动态构造存放学生信息的结构体数组
6、链表
一百五十四、结构体4_结构体的赋值和初始化
1、定义的同时可以整体赋值,如果定义完之后只能单个的进行赋初值;
代码:
#include
struct Student
{
int age;
float score;
char sex;
};
int main(void)
{
//法1:定义的同时进行全部初始化
struct Student st = {18,88.88,‘G’};
//法2:定义和初始化分开进行
struct Student st1;
st1.age = 20;
st1.score = 99.99;
st1.sex = 'B';
//严格说这种先定义再进行全部赋值的方式是错误的,不允许的,程序会弹出警告
struct Student st2;
st2 = {22,77.77,'G'};
printf("%d %f %c\n",st.age,st.score,st.sex);
printf("%d %f %c\n",st1.age,st1.score,st1.sex);
printf("%d %f %c\n",st2.age,st2.score,st2.sex);
}
/*
输出结果:
18 88.879997 G
20 99.989998 B
22 77.769997 G
注意float浮点类型都是不精确的;
弹出警告:
22 6 C:\Users\shu\Desktop\C语言\2.05练习.cpp [Warning] extended initializer lists only available with -std=c++11 or -std=gnu++11
*/
一百五十五、结构体5_如何取出结构体变量中的每一个成员【重点】
1、结构体变量名.成员名 例如 st1.name
2、指针变量->成员名(这种方式更常用)
注意:指针变量->成员名 在计算机的内部会被转化成 (*指针变量名).成员名 的方式来执行;所以说这两种方式是等价的;
代码举例:
#include
struct Student
{
int age;
float score;
char sex;
};
int main(void)
{
struct Student st1;
//第一种方式
st1.age = 20;
//第二种方式
struct Student * pst = &st1;
//表示只能存放Student类型的变量的地址,此处存放的是st1的地址
pst->score = 88.77f;
pst->sex = ‘G’;
printf("%d %f %c\n",st1.age,st1.score,pst->sex); //输出结果:20 88.769997 G
}
注意:pst->score在计算机内部会被转化为 (*pst).score,没有为什么,这就是->的含义,这也是一种硬性规定;所以pst->score等价于(*pst).score等价于st1.score
3、pst->score的含义:
pst所指向的哪个结构体变量种的score这个成员;
*pst代表的就是st;
4、pst->score = 88.77f;
88.77本来是double类型的,而在程序种我们定义的是float类型,所以数据后面需要加上f或F;
由于浮点数都是不精确的,所以输出的只也不是精确的,只能存储近似值;
一百五十六、结构体6_布置作业
动态数组?
一百五十七、考前知识点概述
1、结构体
2、什么是变量?什么是内存?
变量:
定义变量时系统会分配出相应的内存,通过变量我们将数据存储在系统分配的这段内存中,变量代表的就是这段内存空间,变量的本质就是内存中的一段存储空间。
内存:
在计算机的组成结构中,有一个很重要的部分,就是存储器。存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作。
3、为什么需要函数?什么是函数?函数的优点?
需要函数:
当一些相似的功能需要重复实现的时候,避免大量的写重复的代码,将其封装为一个函数,当需要时直接调用即可完成相似的功能操作。
什么是函数:
逻辑上:能够完成特定功能的独立代码单元。
物理上:能够接收数据(也可以不接收);能够对接收的数据进行处理;能够将数据处理的结果返回(也可以不返回)。
总结:函数是一个工具,是为了解决大量类似问题而设计的,函数可以当作黑匣子。
函数的优点:
避免了重复性操作
有利于程序的模块化
4、什么是指针?什么是地址?什么是指针变量?他们之间的关系?
指针就是地址,地址就是指针;
指针变量就是存放内存单元编号的变量或者说指针变量就是存放地址的变量;
指针于指针变量类型不同,指针的本质就是一个操作受限的非负整数;(指针)地址可以进行相减运算,不了可以进行加法,乘法、除法;
5、静态内存?动态内存?区别?
静态内存:
由系统分配,分配好之后中途不可以更改大小,并且程序员不可以手动释放,只能等所在的函数或程序结束之后才可以自动被释放,因为它是在栈中分配的;
动态内存:
利用malloc进行分配,需要引入头文件,分配内存的大小你可以手动控制,动态分配的内存不会因为程序或者函数的结束被释放,只有用free才可进行释放,动态分配的内存可以利用指针进行跨函数的使用,因为它是在堆中分配的;
6、程序流程
读懂一个程序的关键就是要懂得程序的执行流程;
7、C语言的重点:函数、流程控制、指针、动态内存和静态内存
8、代码规范性、什么是ASCII码?
代码规范有助于提高代码的可读性,特别是在协同开发时,便于他人理解;
ASCII是美国标准信息交换代码,它只是一种代号,规定了某一些值用固定的数字来表示,例如:a用95来表示,即a的ASCII码为95;
一百五十八、结构体7_复习上节课知识
1、结构体的复习
#include
struct Student //struct表示定义结构体类型
{
int age;
char sex;
char name[100];
}; //分号不能省略
int main(void)
{
struct Student st1 = {18,‘G’,“姝姝”};
printf("%d %c %s\n",st1.age,st1.sex,st1.name); //输出结果:18 G 姝姝
//%s用于输出字符串
struct Student * pst = &st1;
pst->age = 22;
pst->sex = 'B';
printf("%d %c %s\n",pst->age,pst->sex,pst->name); //输出结果:22 B 姝姝
//pst->age 转换为 (*pst),age等价于st1.age
}
一百五十九、结构体8_通过函数完成对结构体变量的输入和输出
1、指针发送数据的速度很快
注意此处需要用指针来输入或者输出,因为普通变量无法跨函数使用;
2、strcpy((*pst).name,“张三”);
字符串的赋值需要用strcpy来进行赋值。但是使用之前必须要加上头文件 #include
(*pst).name等价于st.name
3、通过函数完成对结构体变量的输入和输出
#include
#include
void InputStudent(struct Student *);//函数声明 ,声明中可以不加上形参名字,但是需要表明存在形参
void OutputStudent (struct Student );
struct Student
{
int age;
char sex;
char name[100];
};
int main(void)
{
struct Student st;
InputStudent(&st);//对结构变量的输入 ,必须发送地址 (因为要修改)
//printf("%d %c %s\n",st.age,st.sex,st.name);//输出结果:18 F 张三
OutputStudent(st);//对结构变量的输出 ,可以发送地址也可以发送内容
}
void InputStudent(struct Student *pst)
{
//pst指向所传入的变量的地址,指针变量的长度是四个字节,只存储数据的起点地址,由类型决定指向的数据到底有多长
//*pst代表的就是st
(*pst).age = 18;
pst->sex = ‘F’;
strcpy((*pst).name,“张三”);
}
void OutputStudent (struct Student stu)
{
printf("%d %c %s\n",stu.age,stu.sex,stu.name);//输出结果 18 F 张三
}
/*
错误代码
注意:要想修改数据的值不可以使用普通的变量,因为普通的变量不支持跨函数使用
void InputStudent(struct Student stu)
{
stu.age = 18;
stu.sex = ‘F’;
strcpy(stu.name,“张三”);
}
*/
一百六十、结构体9_应该发送内容还是应该发送地址【重点】
1、输出函数中可以传入内容或者地址,哪一个比较好呢?
目的:说明指针的优点:执行速度快、耗用内存少、快速传输数据
2、一般如果我们将内容直接作为实参赋值给形式参数处理,那么耗费的内存较大(此处用sizeof来判断所占内存的大小),所以一般我们采取传送指针变量的形式来节约内存,指针变量只占四个字节;
代码:
#include
#include
void InputStudent(struct Student *);//函数声明 ,声明中可以不加上形参名字,但是需要表面存在形参
void OutputStudent (struct Student * );
struct Student
{
int age;
char sex;
char name[100];
};
int main(void)
{
struct Student st;
InputStudent(&st);//对结构变量的输入 ,必须发送地址 (因为要修改)
printf("%d\n",sizeof(st));//输出:108,表示我们定义的st变量的大小
OutputStudent(&st);//堆结构变量的输出 ,可以发送地址也可以发送内容
}
void InputStudent(struct Student *pst)
{
(*pst).age = 18;
pst->sex = ‘F’;
strcpy((*pst).name,“张三”);
}
void OutputStudent (struct Student * pstu)
{
printf("%d %c %s\n",pstu->age,pstu->sex,pstu->name);//输出结果 18 F 张三
}
一百六十一、指针优点大总结【重点】
1、指针的重要性
2、编程的步骤
存储:将需要解决的问题输入计算机
操作:进行数据操作等等
输出:输出结构
3、数据结构就是研究数据的存储和操作,指针是学习数据结构的基础
4、字符串的结束符号是 \0
一百六十二、结构体10_结构体变量的运算
一百六十三、冒泡排序
1、相对查找,排序更加重要;
2、一般都存在排序的函数,直接调用即可;
3、函数中要跨函数操作一定是要以指针的方式传入;
#include
void sort(int * pArr,int len)
{
int i,j,t;
for(i=0;i
for(j=0;j
if(pArr[j] > pArr[j+1])
{
t = pArr[j+1];
pArr[j+1] = pArr[j];
pArr[j] = t;
}
}
}
}
int main(void)
{
int array[6] = {1,0,-5,8,2,10};
sort(array,6);//改变数组的顺序需要传入指针来改变,数组名本身就是指针常量,指向数组的第一个元素的地址
for(int i;i<6;i++)
{
printf("%d ",array[i]);
} //输出结果:-5 0 1 2 8 10
}
4、指针只存放首地址如何确定具体指向的内存的地址是多长?
通过类型来判断长度,就可得到终止地址
一百六十四、结构体11_综合应用_学生管理系统(存储,排序,输出)
1、动态构造一个数组、存放学生的信息,然后按分数排序输出
注意:int * p = (int *)malloc(4); //malloc只有一个形式参数, (int *)用于确定 系统动态分配的内存如何使用 ,每四个字节为一组的去分配
代码:
代码可以继续封装为函数
#include
#include
struct Student
{
char name[100];
int age;
float socre;
};
int main(void)
{
int len;
struct Student * pArr;
printf("请输入学生的个数:\n");
printf("len = ");
scanf("%d",&len);
//动态构造一维数组
pArr = (struct Student *)malloc(len * sizeof(struct Student));
//输入数据:
for(int i=0;i
}
注意:数组即为连续区间内的相同类型的数据,pArr = (struct Student *)malloc(len * sizeof(struct Student));表示分配连续的空间,长度为len * struct Student,类型是struct Student;struct Student *表示以struct Student 长度来分配,并且分配的内存的类型也是struct Student;pArr 是struct Student *类型,因为malloc分配了内存空间后返回的是分配空间的第一个字节的地址;
2、函数封装
#include
#include
struct Student
{
char name[100];
int age;
float socre;
};
void Input(struct Student * Parray1,int length)
{//Parray1代表的就是数组 ,传入的本身就是指针变量
for(int i=0;i
printf(“请输入第 %d 学生的数据:\n”,i+1);//提示用户的输入
printf("name = ");
scanf("%s",Parray1[i].name);
printf("age = ");
scanf("%d",&Parray1[i].age);//scanf需要地址符号 &
printf("score = ");
scanf("%f",&Parray1[i].socre);
}
}
void Output(struct Student * Parray1,int length1)
{
for(int i=0;i
printf(“第 %d 学生的数据:\n”,i+1);
printf("name = %s ",Parray1[i].name);//name本来就是数组,数组名称内部存放的也刚好是第一个元素的地址,所以可以不用加取地址符号 &
printf("age = %d ",Parray1[i].age);
printf(“score = %lf \n”,Parray1[i].socre);
}
}
int main(void)
{
int len;
struct Student * pArr;
printf("请输入学生的个数:\n");
printf("len = ");
scanf("%d",&len);
//动态构造一维数组 ,pArr代表的是数组
pArr = (struct Student *)malloc(len * sizeof(struct Student));
//输入数据:
Input(pArr,len);
printf("\n\n学生数据:\n");
//输出数据:
Output(pArr,len);
}
3、按照输入学生的成绩进行排序
#include
#include
struct Student
{
char name[100];
int age;
float score;
};
void Input(struct Student * Parray1,int length)
{//Parray1代表的就是数组 ,传入的本身就是指针变量
for(int i=0;i
printf(“请输入第 %d 学生的数据:\n”,i+1);//提示用户的输入
printf("name = ");
scanf("%s",Parray1[i].name);
printf("age = ");
scanf("%d",&Parray1[i].age);//scanf需要地址符号 &
printf("score = ");
scanf("%f",&Parray1[i].score);
}
}
void sort(struct Student * Parray2,int length2)
{
struct Student t;
for(int i=0;i
for(int j=0;j
if(Parray2[j].score > Parray2[j+1].score)//比较成绩的大小
{
t = Parray2[j];//t要定义为struct Student类型。否则数据不一致,无法赋值
Parray2[j] = Parray2[j+1];
Parray2[j+1] = t;
}
}
}
}
void Output(struct Student * Parray1,int length1)
{
for(int i=0;i
printf("name = %s ",Parray1[i].name);//name本来就是数组,数组名称内部存放的也刚好是第一个元素的地址,所以可以不用加取地址符号 &
printf("age = %d ",Parray1[i].age);
printf(“score = %lf \n”,Parray1[i].score);
}
}
int main(void)
{
int len;
struct Student * pArr;
struct Student t;
printf("请输入学生的个数:\n");
printf("len = ");
scanf("%d",&len);
//动态构造一维数组 ,pArr代表的是数组
pArr = (struct Student *)malloc(len * sizeof(struct Student));
//输入数据:
Input(pArr,len);
//成绩排序 ,冒泡实现,比较的是成绩的大小,但是交换的是整体的数据的顺序
sort(pArr,len);
printf("\n\n学生数据:\n");
//输出数据:
Output(pArr,len);
}
一百六十五、枚举 上
1、什么是枚举?
把一个事物所有可能的取值一一列举出来
2、怎么样使用枚举?
3、枚举的优缺点?
一百六十六、枚举 下
1、enum代表的就是枚举
2、枚举是一种数据类型,它和结构体定义的方式相似,以enum开头定义,枚举内部列举了该类型的值所有可能的值;
代码:
#include
enum Weekday
{
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
};
int main(void)
{
enum Weekday day = Tuesday;
printf("%d",day);//输出结果:1
}
上述的代码中:
1)、day用于存放当前的日期情况,如果定义为int类型,则它的取值的范围过大。所以我们将其定义为枚举类型,将其可能的七种值列出;
2)、为何返回的是1?
因为虽然我们在枚举内部定义的是确切的值,但是数据是从0开始编号的,Monday的编号是0,但是我们在赋值的时候不可以赋值为0,1,2…因为计算机将数据编号和我们定义的数据没有具体关系;
3)、我们可以将枚举内部的值的起始值进行赋值,那么返回的值就从赋值的编号开始;
4)、enum枚举类型的数据的赋值必须是我们在枚举数据类型内部定义好的,不可以随意赋值;
3、枚举的应用
当我们在输入或者输出的时候我们可以限制输入或者输出的数据的范围和类型,以免用户输入垃圾数据,这样使得代码更加安全;
4、优点、缺点(不常用)
代码更加安全直观
书写麻烦
一百六十七、进制转化
1、生活中的例子?
2、为什么电脑用二进制?
两种状态很好表示、成本低、易于运算;
3、什么叫进制?
二进制 B ;八进制 O ; 十进制 D ; 十六进制 H ;
4、不同进制的表示
5、什么叫N进制?
6、进制对照表
7、进制转换
十进制转其他进制(求余)
代码举例:
#include
int main(void)
{
int i = 88;
printf("%d\n",i); //十进制输出
printf("%0X\n",i); //十六进制输出
printf("%#X\n",i); //十六进制输出
printf("%o\n",i); //八进制输出
/*
输出结果:
88
58
0X58
130
注意:用 %0X 和 %#X 十六进制输出的差别在于,后者会在输出数据的前面加上 0X 来说明输出的数是16进制
*/
}
注意:在C语言总没有二进制输出的标准
8、进制转换2
其他进制转十进制(转换的值*对应进制的对应次数)
9、进制转换3
二进制和十六进制的转化 : 四位一体 2^4=16
(16进制有16种状态,二进制有2种状态,用要二进制来表示16种状态,那么共需要4位二进制)
二进制和八进制的转化 : 三位一体 2^3=8
(8进制有8种状态,二进制有2种状态,用要二进制来表示8种状态,那么共需要3位二进制)
10、进制转换4
一百六十八、补码 上
学习目标:
1)、在C语言种一个int类型的变量所能存储的数字的范围是什么?
2)、最小负数的二进制代码是多少?
3)、最大整数的二进制代码是多少?
4)、已知一个整数的二进制代码,求出原始的数字?
5)、数字超过最大正数会怎么样?
1、原码、补码、反码、移码;
原码:
第一位是符号位(0表示正,1表示负),用于确定正负;其余位表示的就是正常的值;
例如:-5 即为 1101(第一个1表示该数是负数)
0有+0和-0之分
计算机内部不使用
反码:
运算不方便,计算机内部不使用
移码:
用于存储阶码
补码:
在计算机内部,补码用于存储数据(根据补码的格式来存储数据,是一种编码格式)
分为两种:
1)、已知十进制转二进制
case1:求正整数的二进制
case2:求负整数的二进制
(取反即:1改为0,0改为1)
负数的高位是补1,正数高位是补0
case3:求零的二进制
全是0,补码种0的二进制一致
2)、已知二进制转十进制
case1:如果首位是0,则表明是正整数,按普通方法来求;
即普通二进制转换为十进制
case2:如果首位是1,则表面是负整数,将所有位取反,末尾加1,所得数字就是该负数的绝对值(将绝对值取反即可得到原来的数值);
case3:如果对应的全是零,则对应的十进制数字为0
注:
1、什么是编码?
即在计算机内部存储数据的方式;
2、在计算机默认情况下,未写满的数据,计算机会在前面默认补零;
例如:我们定义了一个int类型四个字节32位的内存空间,但是我们存放的数只占了后面的4位, 那么前面空缺的28位计算机会自动补零.
3、再计算机种要遇到首位是1 的情况我们要手动补充,将空余的空间全部补为1;
一百六十九、复习上节课补码知识
1、在计算机中以补码的形式来存储数据;
2、0表示正数,1表示负数;
3、负数转换时在前面补1;
4、补码的转换分为二进制转十进制和十进制转二进制两种;其中又包含正整数和负整数两种,在正整数的转换中只需要直接进行转换即可,负数的转换较为复杂;
一百七十、补码 下
1、8位二进制所代表的十进制 示意图
2、当我们在给变量进行赋值的时候,其实变量内部存储的是我们所赋值的数据二进制的形式,所以我们在用printf输出的时候才需要确定我们需要的输出格式;
代码:
#include
int main(void)
{
//计算机中存储的1000 0000代表的 十六进制是80 十进制是-128
char ch = 0x80;//所赋的值以二进制的形式存放在变量中
printf("%d",ch); //%d表示以十进制的形式输出
//输出结果:-128
}
3、长类型赋值短类型造成的字节丢失
代码举例1:
#include
int main(void)
{
//计算机中存储的1000 0000代表的 十六进制是80 十进制是-128
char ch = 128;//所赋的值以二进制的形式存放在变量中
printf("%d",ch); //%d表示以十进制的形式输出
//输出结果:-128
//128是int(整型)数据,所占的字节的长度是4,但是char类型所占字节是1
//一个字节占8位
//128在计算机内部的表示是(补码方式):0000 0000 0000 0000 0000 0000 1000 0000
//前面的两个字节补0表示该数是正数
//但当我们将四个字节大小的数赋值给一个字节大小的数时,前面的三个字节丢失
//赋值给char类型的一个字节只剩下 1000 0000
//当我们输出时,读到的第一位是1,所以计算机会认为它是一个负数
//根据负数计算补码的规则(取反,加1,得原值的绝对值) ,计算机将该二进制代码解读为-128
}
代码举例2:
#include
int main(void)
{
//计算机中存储的1000 0000代表的 十六进制是80 十进制是-128
char ch = 129;//所赋的值以二进制的形式存放在变量中
printf("%d",ch); //%d表示以十进制的形式输出
//输出结果:-127
//129是int(整型)数据,所占的字节的长度是4,但是char类型所占字节是1
//一个字节占8位
//129在计算机内部的表示是(补码方式):0000 0000 0000 0000 0000 0000 1000 0001
//前面的两个字节补0表示该数是正数
//但当我们将四个字节大小的数赋值给一个字节大小的数时,前面的三个字节丢失
//赋值给char类型的一个字节只剩下 1000 0001
//当我们输出时,读到的第一位是1,所以计算机会认为它是一个负数
//根据负数计算补码的规则(取反,加1,得原值的绝对值)
//取反: 0111 1110 加1:0111 1111 转为十进制(绝对值):127 原值:-127
//计算机将该二进制代码解读为-127
}
4、一个整型(int)所能存储的数字的范围是多少?
int类型变量所能存储的最大正数的二进制表示是:(一个字节占的是8位)(首位为 0 表正数)
0111 1111 1111 1111 1111 1111 1111 1111
转换为十六进是:7FFFFFFF
int类型变量能存储的绝对值最大的负整数二进制表示:(一个字节占的是8位)(首位为 1 表负数)
1000 0000 0000 0000 0000 0000 0000 0000
转换为十六进是:80000000
注意:负数要取反,加1,才能得到原数值的绝对值
5、绝对值最小的负数的二进制代码是多少?
全为1的情况,取反后全为0,加1后形成的值最小;
例如:一个字节的数的最小负数是:1111 1111
首位为1表示负数;
取反:0000 0000
加 1:0000 0001
转换为十进制(原值绝对值):1
原值:-1
6、最大正数的二进制代码是多少?
首位必须为0才能表示整数,并且由正数的转换原则(直接转换)
例如:一个字节的最大的正数的二进制为:0111 1111
转换为十进制:127
7、已知一个整数的二进制代码求出原始的数字
当首位为0时:
表示的是正整数,并且直接转换成十进制即可,例如:0000 0101 即为代表的是5
当首位为1时:
表示的是负数,需要先取反,再加1,得到原数的绝对值,加上符号接口
例如:1110 1110 取反:0001 0001 加1 :0001 0010 转为十进制(绝对值):34 原值:-34
8、数字超过最大正数会怎么样?
当超过的时候就会丢失字节,存放能够存放的长度;
例如:int类型128占四个字节,当我们将其赋值给一个字节的char类型数据时,只有最后的一个字节被赋值给了char,前三个字节填充的0丢失,所以,当以十进制输出时结果为-128,因为char内部存放的是:1000 0000 以1开头,读出时表示的是负数;
9、不同类型数据的相互转化
可能会造成数据字节长度的丢失,数据产生溢出
一百七十一、链表 上
注意:字符串处理是C语言中的一个难点
链表是数据结构的基础
数据存储是数据结构的基本
1、算法:
通俗定义:解题的方法和步骤
狭义定义:对存储数据的操作
广义定义:广义的算法也叫泛型(王非老师的数据结构)
无论数据是如何存储的,对该数据的操作都是一样的,至少我们可以通过两种结构来 存储数据 (数组和链表)
数组:存放连续的数据(在内存中存放的时候也是连续空间)
注意:(数组的缺点)
1)、当我们存放的数据非常大的时候,内存无法找到连续的非常大的一段空闲空间
2)、数组要求内部的所有元素的空间地址是连续的,所有我们删除、插入中间的数据会造成整体的数据的移动,十分的不方便
链表很好的解决了数组的这些问题
一百七十二、链表 中
1、数组的优点:存取熟读快
数组的缺点:需要一个连续的很大的内存、插入和删除元素的效率很低
2、链表
3、链表和数组的区别(链表的优点)?
1)、链表是分开存放的,不需要很大的一块内存空间,可以是碎片化的内存空间组成;
2)、相比数组链表多了一个存放下一个元素地址的空间,相比数组耗用更大的内存空间;
3)、在链表中删除和插入元素相对方便,因为只需要改变链表中上一个元素指向的地址即可;
例如:某一段链表空间的指向顺序是:空间1–》空间2–》空间3–》空间4
若我们要删除空间2中的元素,我们只需要将空间1中的指向地址改为空间3的地址即可;
插入元素同理
4、链表的缺点
1)、数组是在连续空间中存储,我们使用数组的某个元素可以采取 数组名[下标] 的方式来使用某一个元素(因为空间连续),例如: a[2] 可以调用a数组的第三个元素,即等价于 *(a+2);
而链表是不连续空间的存储,不可用该方法调用元素,链表取出元素的效率很低;
5、链表中的一些专业术语
头结点:头结点的数据类型和首节点的类型一模一样;
头结点是首节点前面的哪个节点;
头结点并不存放有效数据;
设置头结点的目的是为了方便对链表的操作;
头指针:存放头结点的地址的指针变量;
首节点:存放第一个有效数据的节点;
尾节点:存放最后一个有效数据的节点;
6、使用链表需要几个参数?
1)、在数组中,数据是连续空间内部存放的,所以我们需要传入数组名(数组名内部就是第一个元素的地址)、数组长度(因为数组的最后一个元素没有标记)
2)、在链表中,空间是不连续的,并且每一个元素都存放了下一个元素的地址,利用指针将整个链表串联起来,尾节点内部存放的地址是null,所有我们只需要知道第一个元素的地址即可,即头指针
原因:头指针内部存放头结点的地址,头结点的内部存放了首节点的地址,首节点的地址依次可以推出尾节点的地址。
7、链表举例
当我们在函数内部创造链表的时候,我们要采取动态创建的方式,因为静态创建的话,函数终止后链表所在的空间也被释放,我们返回的头指针也不存在了;
代码:
#include
//定义一个链表节点的数据类型,结构体实现
struct Node
{
int data;//数据部分
struct Node * pNext;//指向地址部分
};
int main(void)
{
struct Node * pHead;//用于存放头结点的地址(头指针)
pHead = creat list ();//creat list 函数的功能就是创建一个非循环链表 ,并返回链表的头结点的地址
//将头结点的地址存放在pHead中,pHead指向我们在creat list中创建的链表
traverse_list(pHead);//根据所传入的头指针,输出链表
}
注意:内部的创建和输出代码自己写
exit表示程序的终止;
尾节点内部的地址部分是NULL;
使用结构体来实现链表;
一百七十三、链表 下
1、链表分为:数据和指针域两个部分
2、使用链表只需要头结点的地址即可,即知道头指针,就可以确定整个链表
3、任何一个链表都要有头结点,所以我们在创建链表的时候要先创建头结点,无论链表是否为空,链表都要有头结点(便于操作链表)
4、创造好头结点之后我们可以动态生成链表,使用malloc函数创建;
使用for循环逐步创建链表:
第一步:让用户确定需要创建的链表的长度,确定for循环的长度
第二部:将用户每次输入的数据插入到链表当中(每循环依次插入一个)
5、返回我们创建的头结点的地址
6、创建好链表之后,通过遍历输出链表的数据
(在计算链表的长度的时候,头结点是不计算在内的,因为首节点开始存放有效数据)
(链表不是连续空间,数组是连续空间,取数据的时候前者不可以使用下标,不等价于*(a+i))
1)、首先判断链表是否为空?
使用一个函数来判断链表是否为空(需要传如头指针做实参),判断头结点的指针域是否为空,若为空则首节点为空,那么链表就是空;
2)、pHead->pNext 等价于 (*pHead).pNext
指针变量名->数据名 等价于 结构体变量名.数据名
此处该代码表示调用pHead所指向的那个内存空间的pNext的值;pNext就是指针域,用于存放下一个空间的地址;
若该指针域为空(== NULL)那么就是空指针;
3)、当不为空链表时,我们再创建一个指针p指向指针域内部的地址(也就是第二个节点的地址),然后利用p的地址来输出下一个元素的数据值(p->data等价于(*p).data);
再将第二个元素的指针域地址赋值给p,这样p就指向了第三个元素,可以输出第三个数据的值;
以此类推,直到我们找到最后一个尾节点,尾节点的指针域是NULL;
注意:
1)、无论是在创建链表还是输出链表数据时,操作的函数的类型必须要与创建的链表的节点类型一致,例如:链表是struct Node类型,那么creat list()函数应该是struct Node creat list;
2)、当链表是空链表时,不需要进行输出,我们可以采用if语句进行判断是否为空链表,也可以采用while循环来判断链表是否为空;
链表的插入和输出的代码:(要求理解)
注意:函数内外的pHead是不一样的,函数内部的pHead只是与外部pHead同名字,但是内部pHead用于不断的赋值(改变每个节点的指针域的地址),外部main函数中的pHead一直指向的都是头结点
#include
#include
struct Node
{
int data;
struct Node * pNext;
};
void addNode(struct Node *, int);
void showList(struct Node *);
int main(void)
{
char choose;
//定义头指针
struct Node * pHead;
// 初始化头接点,将头结点的地址赋值给头指针,malloc返回的是分配的内存空间的第一个字节的地址
pHead = (struct Node *) malloc(sizeof(struct Node));
//避免情况:如果用户输出的是空链表,而我们没有将头结点pNext初始化,那么头结点中存放的就是一个垃圾值
//垃圾值也可能指向某些内存空间
pHead -> pNext = NULL;//等价于 (*pHead).pNext
//用循环依次插入数据到链表中
do
{
printf("继续添加节点请按y ,否则输出链表\n");
scanf(" %c", &choose);
if (choose == 'y')
{
int data;//接收用户要存放在节点的值
printf("请输入接点值\n");
scanf(" %d", &data);
//创建链表的函数 ,传入的参数是 指向的当前元素的指针 和 当前需要放入链表的数据
//如果要改变链表(或者其他类型)的数据,必须要用指针才能改变(跨函数使用)
addNode(pHead, data);
}
printf("\n\n");
}while (choose == 'y');
showList(pHead);
return 0;
}
//链表输出
void showList(struct Node * pHead)
{
struct Node * pHead1 = pHead;
// 判断节点指针域是否有值
//此程序输出的第一个是垃圾值,因为pHead 指向的是头结点
while (pHead -> pNext != NULL)
{
//非头结点才输出
if(pHead1 != pHead)
{
printf("%d\t", pHead -> data);
}
pHead = pHead -> pNext;
}
if(pHead1 != pHead)
{
printf("%d\t", pHead -> data);
}
}
//链表创建
void addNode(struct Node * pHead, int data)
{
struct Node * newNode;
// 判断当前节点 指针域是否有值
//第一次判断时,判断的是头结点的指针域(开头进行了初始化,所以值为NULL),因此我们需要创建首节点,并且将首节点的地址赋值给头结点的指针域
//除头结点的指针域判断为NULL外,其他情况为NULL的原因只有是遇到最后一个节点的情况
//有值:则使得指针指向下一个空间的地址 ,并将需要插入的数据存入
while (pHead -> pNext != NULL)
{
pHead = pHead -> pNext;//指针指向下一个空间
}
newNode = (struct Node *) malloc(sizeof(struct Node));//在创建一个节点
newNode -> pNext = NULL;//初始化新创建的节点的指针域 ,用于存储下一个节点的地址
newNode -> data = data;//存入数据
pHead -> pNext = newNode;// newNode是一个指针,内部存放的是新创建的节点的地址,该行代码的意义在于:将新创建的节点的地址赋值给了上一个节点的指针域
}
运行结果:
流程:
插入数据的流程:
输出数据的流程:
一百七十四、狭义的算法
对不同的存储结构,要完成某一个功能所执行的操作是不一样的;
例如:要输出数组所有元素的操作和要输出链表所有元素的操作肯定是不一样的;
这说明:算法是依附于存储结构的;不同的存储结构所执行的算法是不一样的;
一百七十五、广义的算法
广义的算法也叫泛型;
泛型:无论数据是如何存储的,对该数据的操作都是一样的;
(其实实际上是不一样的,只是我们采取了一些措施使得操作处理相同,即进行了细节屏蔽)
(例如:输出数组的代码不可以用于输出链表,因为链表不连续,数组连续;但是我们可以在内部对代码进行一些更改,例如使用函数对链表的输出进行处理,屏蔽掉这些细节,使得操作起来一样)
一百七十六、位运算符
1、逻辑运算符
与:&&
或:||
非:!
2、位运算符
1)、& – 按位与(只有 真与真 才得 真 ,1代表真,0代表假)
&& 逻辑与 也叫并且
&& 和 & 的含义完全不同
1&1 = 1
1&0 = 0
0&1 = 0
0&0 = 0
代码:
#include
int main(void)
{
int i = 6;
int j = 10;
int m = -2;
int n = 10;
int k;
//逻辑运算符的结果只能是真或者假,1代表真,0代表假
k = (i == 5) && (j > 10);//&& 与,条件同时成立,返回的是布尔值true(1)或false(0)
printf("%d\n",k);//输出结果:0
k = i & j;//将 i 和 j 的二进制代码进行 相与
printf("%d\n",k);//输出结果:2
k = m & n;//将 i 和 j 的二进制代码进行 相与
printf("%d",k);//输出结果:10
}
结果图:
解析图:
2)、 | 表示 按位或
|| 表示的是逻辑或
|| 和 | 不一样
1 | 1 =1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0
代码:
#include
int main(void)
{
int i = 6;
int j = 10;
int m = -2;
int n = 10;
int k;
//逻辑运算符的结果只能是真或者假,1代表真,0代表假
k = (i == 6) || (j > 10);//|| 或,条件有一个成立即可,返回的是布尔值true(1)或false(0)
printf("%d\n",k);//输出结果:1
k = i | j;//将 i 和 j 的二进制代码进行 相或
printf("%d\n",k);//输出结果:14
k = m | n;//将 i 和 j 的二进制代码进行 相或
printf("%d",k);//输出结果:-2
}
结果图:
解析图:
3)、~ 表示 按位取反(就是把二进制位取反)
! 表示的是逻辑非
! 和 ~ 不一样
代码:
#include
int main(void)
{
int i = 6;
int j = 10;
int k;
//逻辑运算符的结果只能是真或者假,1代表真,0代表假
k =!( (i == 6) || (j > 10) );
printf("%d\n",k);//输出结果:0
k = ~i;//将i的二进制代码取反
printf("%d\n",k);//输出结果:-7
}
结果图:
解析图:
4)、^ 表示按位异或
相同为0
不同为1
1 ^ 0 = 1
1 ^ 1 =0
0 ^ 1 = 1
0 ^ 0 = 0
5)、<< 表示按位左移
i << 1 表示把i的所有二进制位左移一位,右边补零
左移n位相当于乘以2的n次方
面试题:
i = i8;
i = i<<3;
请问上述的两个语句,哪个语句执行的速度更快?
第二种方式更快
6)、>> 表示按位右移
i >>1 表示把i的所有二进制位右移一位,左边一般是补0
(也可能补1,有算术运算和逻辑运算2种情况,根据最高位来)
右移n位相当于除以2的n次方,前提是数据不能丢失(不可溢出)
面试题:
i = i8;
i = i>>3;
请问上述的两个语句,哪个语句执行的速度更快?
第二种方式更快
代码:
#include
int main(void)
{
int i = 6;
int j = 10;
int k;
//左移为乘,右移为除
//例如:十进制左移相当于扩大了十倍(个位->十位) 乘10
// 十进制右移相当于缩小了十倍(十位->个位) 除10
k = i << 1;//左移
printf("%d\n",k);//输出结果:12 6*2=12
k = j >> 1;//右移
printf("%d\n",k);//输出结果:5 10/2=5
}
3、位运算符的现实意义?
通过位运算符我们可以对数据的操作精确到每一位(二进制位)
一百七十七、文件 宏 typedef 漫谈[课程全部结束]
1、文件
2、宏
3、typedef
一百七十八、C期末考试测试题讲解 上
1、什么叫内存分配?什么叫释放内存?
分配内存:操作系统把某一块内存空间的使用权限分配给该程序;
释放内存:操作系统把分配给该程序的一块内存空间的使用权限收回,该程序就不能再使用这一 块内存空间,这叫释放内存;
附注:释放内存不是把该内存的内容清空;
内存本身的控制权限在于操作系统;
2、变量为什么必须得初始化?
不初始化则变量通常就是垃圾值;
因为不同的程序使用完内存空间之后,并不会进行空间的清理,所以会存在垃圾数据,当下一次再次分配给另一个程序空间时,该内存空间可能仍然存在上一个程序留下的垃圾数据;
3、详细说明系统是如何执行:int i = 5; 这个语句的
1)、软件请求操作系统对i分配存储空间
2)、操作系统会再内存中寻找一块空闲的区域,把该区域当作i来使用
3)、程序会把i和这块空闲区域关联起来,今后对i操作就是对这个空闲区域的操作
3)、将5存储到i所关联的内存区域中
附注:所谓内存区域也就是内存中的一块存储单元
4、请详细列出C语言所有的基本类型
int
long int
short int
char
float
double
5、再printf函数中int用%d输出,请问:long int 、 char、 double 、float 分别用什么输出?
%ld %c %lf %f
6、函数的优点是什么 ?
1)、避免重复性操作(减少代码量)
2)、有利于程序的模块化
7、谈谈你对函数的理解?
逻辑上:能够完成特定功能的独立代码单元
物理上:能够接收数据(也可以不接收);能够对接收的数据进行处理;能够将数据处理的结果返回(也可以不返回)
总结:函数是一个工具,是为了解决大量类似问题而设计的,函数可以当作黑匣子;
8、什么是指针、什么是地址、什么是指针变量、三者之间的关系是什么?
地址是内存单元的一个编号,但是可以将地址存放再变量中;
指针就是地址,指针和地址是同一个概念;
指针变量是存放内存单元编号(地址)的变量;
指针变量和指针是两个不同的概念,只不过通常把指针变量简称为指针;
9、静态变量和动态变量的异同?
相同点:都需要内存分配
不同点:
静态变量是系统自动分配的,自动释放,程序员无法在程序运行过程中手动分配或者手动释放,静态内存是在栈中分配的,函数终止之后,静态变量所分配的空间才会被系统释放;
动态内存是程序员手动分配,手动释放,程序员可以在程序运行过程中手动分配或者手动释放,动态内存是在堆中分配的,程序员可以在函数执行的任何时候手动释放动态内存,而不需要等到函数终止才释放,调用free()即可;
10、C语言中那些知识是我们学习 重点,请一一列举出来?
流程控制
函数
指针
静态内存和动态内存
11、流程控制部分
一共有两个语句,B不属于for循环
12、
13、
调整代码结构:
break用于跳出for循环或者switch语句;
break语句只能终止最里面包裹它的哪个循环;
14、
A项:p没有进行初始化,指针指向的是一个垃圾值的地址,所以不可以对*p进行操作
B项:char类型,单个字符要放在单引号内部,字符串放在双引号内部
C项:p用于存放整型的数据类型的地址
D项:p是int 类型,&p是int **类型,所以可以赋值给q
E项:当p进行初始化之后,可以对p的内容进行操作
15、答案:C、D
else只能对应离它最近的哪个if;
要注意:如果if语句后面加了分号表面是空语句,如果没加分号,则if后的第一条语句为if条件的内部语句;
D项:整理好的代码
16、答案:B
17、输出 # 的次数是 6
18、答案:D
s+=i+1 等价于 s=s+(i+1)
程序执行流程:
一百七十九、NULL的含义
1、二进制全部为零的含义------00000000000000000000000 的含义
1)、表示数值零
2)、表示字符串结束标记 " \0 "
3)、空指针NULL
NULL本质也是零,这个零不代表数字,表示的是内存单元的编号零
计算机规定,以零号为编号的存储单元的内存内容不可读不可写
该部分内存计算机用于存放重要数据,用户不可以访问(例如操作异常的检测等等)
一百八十、C期末考试测试题讲解 下
19、答案:A
q赋值为NULL,NULL是零开头的内存空间,不可以进行存放或者读取操作
20、答案:A
21、输出结果:##2##4
输出控制符(%d)前面的字符是原样输出的
22、答案D
整理后代码:(两个switch语句嵌套)
#include
int main(void)
{
int x=1, y=0, a=0, b=0;
switch(x)
{
case 1:
switch(y)
{
case 0:
a++;
break;
case 1 :
b++;
break;
}
case 2:
a++;
b++;
break;
case 3:
a++;
b++;
}
printf(“a=%d, b=%d\n”,a, b) ;
}
注意:
没有break语句终止时,switch语句将会继续按照代码的顺序执行直到代码执行完毕或者遇到break;
23、答案:144 64
十进制转其他进制,除以进制基数,余数倒叙即可
24、答案:C
25、答案:A
1、函数内部不可以定义函数,函数等级相同,不可以嵌套定义,但是可以在函数的内部调用函数;
2、main函数唯一,但是位置不一定在前面,可以将main函数定义在最后面,在前面进行声明;
3、所有被调用的函数如果写在程序最后,但是前面的程序需要调用,需要在最前面进行函数声明;
4、函数的声明必须要函数名一致,表明函数是否有参数,有几个参数,参数的值可以不写;
26、编程:求出1+2+3+。。。。。+1000的和
代码:
#include
int main(void)
{
int i;
int sum = 0;//注意sum需要进行初始化
for(i=1;i<=1000;i++)
{
sum = sum+i;
}
printf("%d",sum);
}
结果:
27、编程:求出1+1/2+1/3+1/4+。。。。。。+1/100的和
代码:
#include
int main(void)
{
int i;
float sum = 0;
for(i=1;i<=100;i++)
{
sum = sum+1.0/(i+1);
}
printf("%f",sum);//输出结果:4.197278
}
注意:只有当除数或者被除数之中有一个是小数的时候,相除的结果才是小数,否则两边如果是正数,那么相除的结果会被取整
即:
1/2=0(本来应该是0.5,但是正数除法的结果会被取整)
1.0/2=0.5
28、编一程序,要求完成如下功能:
求1到100之间偶数的平均值,偶数的个数,奇数的平均值,奇数的个数,
并分别在显示器上输出这些值(即把1到100之间偶数的平均值,偶数的个数,奇数的平均值,奇数的个数全部输出到显示器上)
要求:
偶数的平均值用ave1表示 偶数的个数cnt1表示
奇数的平均值用ave2表示 奇数的个数cnt2表示
代码:
注意:可以用for语句分别实现,也可以用if语句判断实现
#include
int main(void)
{
int i,j;
int cnt1 = 0;
int cnt2 = 0;
int sum1 = 0;
int sum2 = 0;
float ave1 = 0;
float ave2 = 0;
for(i=1;i<=100;i++)
{
if(i%2 == 0)//偶数
{
cnt1++;//求出偶数个数
sum1 = sum1 +i;
}
else{//奇数
cnt2++;//求出偶数个数
sum2 = sum2 +i;
}
}
//求平均数
ave1 = (0.1*sum1) / cnt1;
ave2 = (0.1*sum2) / cnt2;
printf("sum1 = %d sum2 = %d\n",sum1,sum2);
printf("cnt1 = %d ave1 = %f\n",cnt1,ave1);
printf("cnt2 = %d ave2 = %f\n",cnt2,ave2);
}