C语言学习
第一周:
人:What to do
计算机: How to do
计算机语言
- 程序是用特殊的编程语言写出来表达如何解决问题的。
- 不是用来和计算机交谈,而是描述要求它如何做事情的过程和方法。
算法
- 我们要让计算机做计算,就要想出来它的计算步骤,然后用编程语言写出来
- 计算机做的所有事情都叫做计算
- 计算的步骤就是算法
程序的执行
- 解释:借助一个程序,那个程序能试图理解你的程序,然后按照你的要求执行
- 编译:借助一个程序,就像一个翻译,把你的程序翻译成计算机真正能懂的语言-----机器语言-------写的程序,然后,这个机器语言写的程序就能直接执行了。
C语言
- C语言是从B语言发展而来的,B语言是从BCPL发展而来的,BCPL是从FORTRAN发展而来的。
- BCPL和B都支持指针间接方式,所以C也支持了
- C语言还受到了PL/I的影响,还和PDP-11的机器语言有很大关系
- 1973年3月,第三版的Unix上出现了C语言的编译器
- 1973年11月,第四版的Unix(System Four)发布了,这个版本是完全用C语言重新写的。
C语言用在哪里
- 操作系统
- 嵌入式系统
- 驱动程序
- 底层驱动
- 图形引擎、图像处理、声音效果
- 开发效率>>学习过程
- 开发效率>>开发乐趣
- 日常应用很少直接用C语言编写
- 学习C语言的过程主要是练习写代码
- 而非真实的软件
编译---->运行
- C语言需要被编译才能运行,所以你需要
编辑器
编译器 - 或者·,IDE(集成开发环境)
第二周:
做计算
printf("23+43=%d\n",23+43);
算找零钱
需要:
- 有地方放输入的数字
- 有办法输入数字
- 输入的数字能参与计算
如何能在程序运行时,输入那个数字 23 ,然后计算输出结果?
int price=0;
prinf("请输入金额(元):");
scanf("%d",&price);
int change=100-price;
printf("找您零钱为:%d",change);
变量
- int price=0;
- 这一行定义了一个变量。变量的名字是price ,类型是 int ,初始值是 0
- 变量是一个保存数据的地方,当我们需要在程序中保存数据时,比如上面的例子中,要记录用户输入的价格,就需要一个变量来保存他,用一个变量保存了数据,它才能参与到后面的计算中,比如算找零钱。
变量的定义
- 变量定义的一般格式为:
- < 类型名称 > < 变量名称 >;
- int price;
- int amount;
- int price,amount;
变量的名字
- 变量需要一个名字,变量的名字是一种“标识符”,意思是它是用来识别这个和那个的不同的名字
- 标识符有标识符的构造规则,基本的组成是:标识符只能由字母、数字、下划线组成,数字不能出现在第一个位置上,C语言的关键字不可以作为标识符。
C语言的关键字(部分)
auto,break,case,char,const,continue,default,do,double,else
enum,extern,float,for,goto,if,int,long,register,return,short
signed,sizeof,static,struct,switch,typedef,union,unsigned,void
volatile,while,inline
赋值和初始化
- int price=0;
- 这一行,定义了一个变量。变量的名字是 price ,类型是 int ,初始值是 0
- price =0 是一个式子,这里的“=”,是一个赋值运算符,表示将“=”,右边的值赋给左边的变量
赋值
- 和数学不同,a=b,在数学中表示关系,既a和b的值一样;而在程序设计中,a=b表示计算机做一个动作:将b的值赋值给a。关系是静态的,而动作是动态的。在数学中,a=b和b=a是完全相同的,而在程序设计中,两者的意思完全相反。
读整数
- scanf("%d",&price);
- 要求scanf函数读入下一个整数,读到的结果赋值给变量price
- 小心price前面的&
常量
- int change=100;
- 固定不变的数,是常数。直接写在程序里面,我们称之为直接量(literal)
- 更好的方式,是定义一个常量。
- const int AMOUNT=100;
- 要在C99里面
#include <stdio.h>
int main()
{
const int AMOUNT=100;
int price=0;
printf("请输入金额(元):");
scanf("%d",&price);
int change=AMOUNT-price;
printf("找您%d元。",change);
return 0;
}
const
- const是一个修饰符,加在int的前面,用来给这个变量加上一个const(不变的)的属性。这个const的属性表示这个变量的值一旦被初始化,就不能在修改了。
- int change = AMOUNT-price;
- 如果你试图对常量进行修改,把它放在赋值运算符左边,就会被编译器发现,指出为一个错误。
浮点数
- 带小数点的数值,浮点数这个词的本意就是指小数点是浮动的,是计算机内部表示非整数(包含分数和无理数)的一种方式,另一种叫做定点数,不过在C语言中,你不会遇到定点数。人们接用浮点数这个词来代表所有的带小数点的数。
double
- double的意思就是双,它本来是“双精度浮点数”的第一个单词,人们用来表示浮点数类型。除了double还有float,(意思就是浮点!)表示单精度浮点数。
- float:单精度浮点数
- double:双精度浮点数
数据类型
- 整数
- int
- printf(“%d”,…);
- scanf("%d",…);
- 带小数点的数
- double
- printf(“%f”,…);
- printf("%lf",…);
表达式
- 一个表达式时一系列运算符和算子的组合,用来计算一个值。
- amount=x*(1+0.0003)(1+0.33)(2+5.6621);
- total=57;
- count=count+1;
运算符
- 运算符(operator)是指进行运算的动作,比如加法运算符“+”,减法运算符“-”
- 算子(operand)是指参与运算的值,这个值可能是常数,也可能是变量,还可能是一个方法的返回值。
- ++ :自增运算符
- – :自减运算符
- & :取地址运算符
- ! :逻辑非运算符
计算时间差
int hour1,minute1;
int hour2,minute2;
scanf("%d %d",&hour1,&minute1);
scanf("%d %d",&hour2,&minute2);
int t1=hour1*60+minute1;
int t2=hour2*60+minute2;
int t=t1-t2;
printf("时间差是%d小时%d分",t/60,t%60);
运算符优先级
优先级 | 运算符 | 运算 | 结合关系 | 举例 |
---|---|---|---|---|
1 | + | 单目不变 | 自右向左 | a*+b |
1 | – | 单目取反 | 自右向左 | a*-b |
2 | * | 乘 | 自左向右 | a*b |
2 | / | 除 | 自左向右 | a/b |
2 | % | 取余 | 自左向右 | a%b |
3 | + | 加 | 自左向右 | a+b |
3 | – | 减 | 自左向右 | a-b |
4 | = | 赋值 | 自右向左 | a=b |
赋值运算符
- 赋值也是运算,也有结果
- a=6的结果是a被赋予的值,也就是6
- a=b=6 等价于 a=(b=6)
交换两个变量
- 如果已经有
- int a=6;
- int b=5;
- 如何交换a、b两个变量的值?
程序是按步执行的
- 程序表达的是顺序执行的结果,而不是关系
- a=b;
- b=a;
- 是依次执行的,结果使得a和b都得到b原来的值
复合赋值
- 5个算是运算符 + - * / % ,可以和赋值运算符 “ = ”,结合起来,形成复合赋值运算符 += -= *= /= %=
- total+=5;
- total=total+5;
- 注意两个运算符中间不要有空格。
递增递减运算符
- ++ 和 - - 是两个很特殊的运算符,他们是单目运算符,这个算子还必须是变量。这两个运算符分别叫做,递增和递减运算符。他们的作用就是给这个变量+1或者-1
- count++;
- count–;
- count=count+1;
自增运算符前缀后缀
- ++ - - 可以放在变量的前面,叫做前缀形式,也可以放在变量的后面,叫做后缀形式。
- 无论是前缀或者后缀,都是使得自己本身的值加一或者减一
第三周
条件
- 计算两个值之间的关系就叫做 关系运算
- 常见的关系运算符
- ==
- !=
- <
- <=
关系运算的结果
- 当两个值符合关系运算符的预期时,关系运算的结果为 1 ,否则为0
printf("%d\n",5==3);
printf("%d\n",3>5);
练习:找零计算器
int price=0;
int bill=0;
printf("请输入金额(元)");
scanf("%d",&price)
printf("请输入票面(元)");
scanf("%d",&bill);
printf("应该找您:%d 元",bill-price);
注释
- 注释插入在程序代码中,用来向读者提供解释信息,它们对于程序的功能没有任何影响,但是往往能使得程序更容易被人类读者理解。
if语句
- 判断大小
- 方案1
int a,b;
printf("请输入两个整数");
scanf("%d %d",&a,&b);
int max=0;
if(a>b){
max=a;
}
if(b>a){
max=b;
}
printf("最大的那个数是 %d",max);
- 方案2
int a,b;
printf("请输入两个整数");
scanf("%d %d",&a,&b);
int max=0;
if(a>b){
max=a;
}else{
max=b;
}
printf("最大得那个数是 %d",max);
- 方案3
int a,b;
printf("请输入两个整数");
scanf("%d %d",&a,&b);
int max=b;
if(a>b){
max=a;
}
printf("最大的那个是%d",max);
- 一个基本的if语句由一个关键字 if 开头,跟上在括号里的一个表示条件的逻辑表达式,然后是一对大括号“{ }”,之间的若干条语句。如果表示条件的逻辑表达式的结果不是0,那么就执行跟着括号里面的若干条语句,否则就跳过这些语句,不执行,而继续执行下面的语句。
- 练习输入成绩,给成绩分等级。
嵌套的判断
- 当 if 的条件满足或者不满足的时候要执行的语句,也可以是一条 if 或者if - else语句,这就是嵌套的 if 语句。
else的匹配问题
- else总是和最近的那个 if 匹配
- 缩进格式不能暗示else的匹配
- 好习惯:在 if 或者 else 后面总是有{ },即使只有一条语句。
- 练习级联的 else-if
if 常见的错误
- 忘了大括号
- if 后面加了分号
- 错误使用 == 和 =
- 使人困惑的 else
代码风格
- 在if 和 else后面必须加上大括号形成语句块
- 大括号内的语句缩进一个 tab 的位置
switch-case
- 练习玩级联的判断成绩等级 使用if语句之后
- 使用switch 实现多分支。
switch(type){
case 1:
printf("您好");
break;
case 2:
printf("您真棒");
break;
}
- 控制表达式只能是整数型的结果
- 常量可以是常数,也可以是常数计算的表达式
break
- switch语句可以看作是一种基于计算的跳转,计算控制表达式的值后,程序会跳转到相匹配的case处,分支标号只是说明switch内部位置的路标,在执行完分支中的最后一条语句后,如果后面没有break,就会顺序执行到下面的case里去,直到遇到一个break,或者指导switch 语句结束为止。
第四、五周
循环
- for 循环
- for ( ; ; ){ }
- while ( 真或者假 ) { }
- do { }while( 真或者假)
- do-while循环和while循环很像,区别是,循环体至少执行的次数,也跟条件判断的位置来决定的,while循环,进入循环体之前就进行了判断,所以执行次数最少为 1 次,而 do-while循环,是先进入循环体,然后在判断,所以循环体执行的次数最少为1 次。
如何选择循环
- 如果有固定的循环次数,用 for
- 如果必须执行一次,用do-while
- 其他情况用while
练习猜数游戏
- 让计算机来想一个数,然后让用户来猜,用户每输入一个数,就告诉他输入是大了还是小了,指导用户猜对为止,最后还要告诉用户总共猜了多少次。
- 因为需要循环让用户进行猜测,所以用到循环。
- 核心的重点是循环的条件。
1、计算机随机想一个数,记在变量number里
2、一个负责统计次数的变量account 初始化为0
3、让用户输入一个数字a
4、account自增
5、判断a和number是否相等,如果大就说大,小就说小,相等就跳出循环
随机数
- 每次召唤 rand() 就得到一个随机的整数
练习整数的逆序输出
练习用for循环写一个阶乘
break和continue
- break:跳出循环
- continue:结束本次循环,进入下一次循环
- 都只能对它所在的那一层去做
- 可以使用goto语句来跳出多重循环
循环的嵌套
- 练习输出100以内所有的素数
- 循环里面又有循环成为循环嵌套
第六周
C语言是有类型的语言
- C语言的变量必须在使用之前定义,并且确定类型
C语言的类型
- 整数
*char 、short 、int、long、long long - 浮点数
*float、double、long double - 逻辑
*bool - 指针
- 自定义类型
类型有何不同
- 类型名称:int、long、double
- 输入输出时候的格式化:%d、%ld、%lf
- 所表达的范围:char<short<int<float<double
- 内存中所占据的大小:1个字节到16个字节
- 内存中表达的形式:二进制(补码)、编码
sizeof
- 是一个运算符,可以给出某个类型或者某个变量在内存中所占据的字节数
- sizeof(int)
- sizeof(i)
整数的范围
- char 1字节 -128~~127
- short 2字节 -32768~~32767
- int 取决于编译器,通常的意义是“1个”字
- long 4个字节
- long long 8个字节
8进制和16进制
- 一个以0开始的数字字面量是8进制
- 一个以0x开始的数字字面量是16进制
- %o 用于8进值,%x用于16进值
浮点类型
类型 | 字长 | 有效数字 |
---|---|---|
float | 32 | 7 |
double | 64 | 15 |
浮点的输入输出
类型 | scanf | printf |
---|---|---|
float | %f | %f |
double | %lf | %f |
输出的精度
- 在% 和f之间加上 .n 可以指定小数点后几位,这样的输出是做4舍5入的
超过范围的浮点数
- printf输出 inf 表示超过范围的浮点数
- printf输出 nan 表示不存在的浮点数
浮点运算的精度
- 带小数点的字面量是double而不是float
- float需要用f或者F后来点缀表明身份
选择浮点类型
- 如果没有特殊需要,直接用double
- 现代cpu能够轻松胜任
字符类型
- char是一种整数,也是一种特殊的类型:字符,这是因为,用单引号表示的字符字面量 ’ a ‘ ,’ 1 ‘
- ’ ‘也是一个字符
- printf和scanf里面用%c来输入输出字符
逃逸字符
字符 | 意义 | 字符 | 意义 |
---|---|---|---|
\b | 回退一格 | " | 双引号 |
\t | 到下一个表格为=位 | \ ’ | 单引号 |
\n | 换行 | \ \ | 反斜杠本身 |
\r | 回车 |
自动类型转换
- 当运算符的两边出现不一样的类型时,会自动转换成较大的类型
- 大的意思是能表达的数的范围更大
- char --》short----》int-----》long----》long long
- int----》float-----》double
强制类型转换
- 要把一个量强制转换成另一个类型,需要:
- (类型)值
bool
- #include < stdbool.h>
- 之后就可以使用bool和true、false
逻辑运算
- 逻辑运算是对逻辑量进行的运算,结果只有0或1
- 逻辑量是关系运算或逻辑运算的结果
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
! | 逻辑非 | !a | 如果a是true则结果为false |
&& | 逻辑与 | a&&b | a和b必须都是true才能是true |
逻辑或 |
表达在4和6之间
- 错误
*4<x<6 - 正确
*x>4&&x<6
优先级
- !> && > 逻辑或
短路
- 逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算。
条件运算符
- count=(count>20)?count-10 : count+10;
- 条件、条件满足时候的值、条件不满足时候的值
逗号运算符
- (a+b,b+c,c+d)
第7周:
什么是函数
- 函数是一块代码,接收零个或者多个参数,做一件事情,并返回零个或一个值
调用函数
- 函数名(参数值);
- ()起到函数调用的重要作用
- 即使没有参数也需要()
- 如果有参数,则需要给出正确的数量和顺序
- 这些值会被按照顺序,依次用来初始化函数中的参数
从函数中返回值
- return 停止函数的执行,并返回一个值
- return ;
- return 表达式;
- 一个函数里,可以出现多个return 语句
没有返回值的函数
- void 函数名(参数表)
- 不能使用带值得return
- 可以没有return
- 调用的时候不能做返回值的赋值
函数的先后关系
- 像这样把sum()写在上面,是因为:
- C语言的编译器自上而下顺序分析你的代码
函数原型
- 函数头,以分号结尾构成了函数的原型
- 函数原型的目的是告诉编译器这个函数长什么样
- 名称
- 参数(数量及类型)
- 返回类型
类型不匹配
- 调用函数时给的值与参数的类型不匹配,是C语言传统上的最大的漏洞
- 编译器总是悄悄地替你把类型转换好,但这可能不是你所期望的
交换 a 和 b的值
- 用函数交换 a 和 b的值
传值
- 每个函数有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系
- 对于参数表中的参数,叫做“形式参数”,调用函数时候给的值,叫做“实际参数”
本地变量
- 函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量
- 定义在函数内部的变量就是本地变量
- 参数也是本地变量
- 也叫做局部变量
变量的生存期和作用域
- 生存期:什么时候这个变量出现了,到什么时候这个变量消亡了
- 作用域:在什么范围内可以访问这个变量
本地变量的规则
- 本地变量是定义在块内
*可以定义在函数的块内
*也可以定义在语句的块内
*甚至可以随便拉一个大括号来定义本地变量 - 程序进入这个块之前,其中的变量不存在,离开这个块,其中的变量就消失了
- 块外面定义的变量在里面仍然有效
- 块里面定义的了和外面同名的变量,则掩盖外面的
- 不能在同一个块里面定义同名的变量
- 本地变量不会被默认初始化
函数里的函数?
- C语言不允许嵌套函数
第八周
如何记录很多数?
- 数组
- int number[100];
- scanf("%d",&number[ i ]);
定义数组
- < 类型> 变量名称【元素数量】
- int grades[100];
- double weight[100];
- 元素数量必须是整数
数组
- 是一种容器(放东西的东西),特点是其中元素具有相同的数据类型一旦创建就不能改变大小
- 数组的元素在内存中是连续依次排列的
数组的单元
- 数组的每个单元就是数组类型的一个变量
- 使用数组时放在【】中的数字叫做下标或索引,下标从0开始计数
- garde[0]
- grade[1]
计算数组的大小
- sizeof(a)/sizeof(a[0])
第九周
指针
- &是一个运算符,是用来获得变量的地址,它的操作数必须是变量
- 就是保存地址的变量
指针变量
- 变量的值是内存的地址
- 普通变量的值是实际的值
- 指针变量的值是具有实际变量值的地址
数组变量是特殊的指针
- 数组变量本身表达地址,所以
- int a[10] ; int *p=a ; 不需用&
指针是const
- 表示一旦得到了某个变量的地址,不能在指向其他变量
指针的类型转换
- void *表示不知道指向什么东西的指针
- 指针也可以转换类型
第十周
字符串
- 字符数组
- char word [ ]={‘H’,‘e’,‘l’,‘l’,‘o’}
- 上面不是字符串,而是字符数组
- 字符串; char word [ ]={‘H’,‘e’,‘l’,‘l’,‘o’,’\0’}
- 字符串是以0结尾的一串字符
- 字符串以数组的形式存在,以数组或者指针的形式访问
- string.h 头文件里面有很多处理字符串的函数