跳转至

📘 C语言学习笔记

1. 计算机与编程语言 🖥️

一.

  • 程序是用特殊的编程语言表达了如何解决问题的
  • 不是用编程语言来和计算机交谈,而是描述要求它如何做事情的过程或方法

二. 算法

  • 我们要让计算机做计算,就需要像这样找出计算的步骤,然后用编程语言写出来
  • 计算机做的所有的事情都叫做计算
  • 计算的步骤就是算法

2. 计算机的思维方式 🧠Von Neumann architecture diagram的图片Shutterstock探索

一. 程序的执行

  • 解释:借助一个程序,那个程序能试图理解你的程序,然后按照你的要求执行
  • 编译:借助一个程序,就像一个翻译,把你的程序翻译成计算机真正能懂的语言--机器语言--写的程序,然后这个机器语言写的传给你就能直接执行了

3. 为什么是C语言 ❓

一. 其他语言

  • 现代的变成语言在语法上的差异很小
  • 几乎都是C-like语言
  • 语言的能力/适用领域主要是由库和传统所决定的

4. C语言的简史 📜

一.

  • C语言是从B语言发展而来的,B语言是从BCPL发展而来的,BCPL是从FORTRAN发展而来的
  • BCPL和B都支持指针间接方式,所以C也支持了
  • C语言还收到了PL/I的影响,还和PDP-II的机器圆有很大的关系
  • 1973年3月,第三版的Unix上出现了C语言的编译器
  • 1973年11月,第四版的Unix(System Four)发布了,这个版本是完全用C语言重新编写的

二. C的发展与版本

  • 经典C
  • 又被叫做"K&R the C"

三. C的发展与版本-标准

  • 1989年ANSI发布了一个标准--ANSI C
  • 1990你那ISO接受了ANSI的标准--C89
  • C的标准在1995年和1999年两次更新--C95和C99

5. 编程软件 🛠️

一. C语言是一种工业语言

  • 操作系统:开发效率 >> 学习过程
  • 嵌入式系统:开发效率 >> 开发乐趣
  • 驱动程序:日常应用很少直接用C语言编写
  • 底层驱动:学习C的过程主要是练习写代码
  • 图形引擎、图像处理、声音效果:而非真是软件

二. 编译-→运行

  • C需要被编译才能运行,所以你需要:
  • 编辑器
  • 编译器
  • 或者,IDE(集成开发环境)

三. 推荐的编程软件

  • Dev C++
  • 免费
  • 安装简单
  • 不用建工程

6. 第一个C程序 🐣

一. 代码示例

C

#include<stdio.h>
int main(){
    printf("Hello World!\n");
    return 0;
}

二. 程序的主干

C

#include<stdio.h>
int main(){

    return 0;
}
  • 本课程中所有的程序都需要这一段,直到学到函数之前,我们的代码都只是在这个框架中间

三. 输出

  • pirntf("Hello World!\n");
  • ""里面的内容叫做“字符串”,printf会把其中的内容原封不动地输出
  • \n表示需要在输出的结果后面换一行

四. 程序中的错误

  • 编译的时候发现的错误所在的地方会以红色的底标示出来
  • 具体的错误原因列在下方的窗口里(是英文的)
  • C的编译器给出的错误提示往往不那么好“猜”

五. 不要用中文

7. 做点计算 ➕

一.

  • printf("%d\n",23+43);
  • %d说明后面有一个整数要输出在这个位置上
  • printf("23+43=%d\n",23+43);

二. 四则运算

四则运算 C符号 意义
+ + 加
- - 减
× × 乘
÷ / 除
% 取余
() () 括号
  • %表示取两个数相除以后的余数

8. 变量定义 📦

一. 如何输入

  • 输入也在终端窗口中
  • 输入是以行为单位进行的,行的结束标志就是你按下了回车键。在你按下回车之前,你的程序不会读到任何东西

二. 变量

  • int price =0;
  • 这一行定义了一个变量,变量的名字是price,类型是int,初始值是0。
  • 变量是一个保存数据的地方,当我们需要在程序里保存数据时,比如上面的例子中要记录用户输入的价格,就需要一个变量来保存它。用一个变量保存了数据,它才能参加到后面的计算中,比如计算找零

三. 变量定义

  • 变量定义的一般形式就是:类型名称 变量名称;
  • int price;

四. 变量的名字

  • 变量需要一个名字,变量的名字是一种“标识符”,意思是它是用来识别不同的名字
  • 标识符有标识符的构造规则。基本的原则是:标识符只能由字母、数字和下划线组成,数字不可以在第一个位置上,C语言的关键字(有的地方叫它们保留字),不可以用做标识符

五. C语言的关键字

  • 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,while。不需要背诵

9. 变量赋值和初始化 📥

一.

  • int price=0;
  • 这一行,定义了一个变量,变量的名字是price,类型是int,初始值是0.
  • price=0是一个式子,这里的“=”是一个赋值运算符,表示将“=”右边的值赋给左边的变量

二. 赋值

  • 和数学不同,a=b在数学中表示关系,即a和b的值一样;而在程序设计中,a=b表示要求计算机做一个动作:将b的值赋个a。关系是静态的,而动作是动态的。在数学中,a=b和b=a是等价的,而在程序设计中,两者的意思完全相反。

三. 初始化

  • 虽然C语言并没有强制要求所有的变量都在定义的地方做初始化,但是所有的变量在第一次被使用(出现在赋值运算符的右边)之前被应该赋值一次。如果没有被赋初值,那么它的内存中有啥就表示啥

四. 变量初始化

  • 类型名称 变量名称 =初始值
  • int price =0;
  • 组合变量定义的时候,也可以在这个定义中单独给变量赋初值

五. 表达式

  • “=”是赋值运算符,有运算符的式子就叫做表达式

六. 变量类型

  • int price=0;
  • 这一行,定义了一个变量。变量的名字是price,类型是int,初始值是0
  • C是一种有类型的语言,所有的变量在使用之前必须定义或声明,所有的变量必须具有确定的数据类型。数据类型表示在变量中可以存放什么样的数据,变量中只能存放指定类型的数据,程序运行过程中也不能改变变量的类型

七. 第二个变量

  • int change =100-price;
  • 定义了第二个变量change
  • 并且做了计算
  • C99版本可以在任意地方定义变量,ANSI C只能在代码开头的地方定义变量

八. 读整数

  • scanf("%d",&price);
  • 要求scanf这个函数读入下一个整数,读到的结果赋值给变量price
  • 小心price前面的&

10. 常量与变量 💎

一. 常量

  • int change = 100 -price;
  • 固定不变的数,是常数。直接写在程序里,我们称作直接量
  • 更好的方式,是定义一个常量 const int AMOUNT=100;

二. const

  • const是一个修饰符,加在int的前面,用来给这个变量加上一个const(不变的)的属性。这个const的属性表示这个变量的值一旦初始化,就不能修改了
  • int change=AMOUNT-price;
  • 如果你试图对常量做修改,把它放在赋值运算符的左边,就会被编译器发现,指出为一个错误

三. try

  • 让用户输入变量AMOUNT的值,而不是使用固定的初始值
  • 这个变量在哪里定义合适呢

11. 关于scanf ⌨️

一.

C

#include<stdio.h>
int main(){
    int a=0,b=0;
    scanf("%d,%d",&a,&b);
    printf("%d,%d",a,b);
    return 0;
}
  • 结果为a,0(如果没有按照双引号内的格式输入,scanf会出错)(scanf的引号里面是用户一定要输入的东西)

12. 浮点数 🌊

一. 计算身高的程序

C

printf("请分别输入身高的英尺和英寸,""如输入\5"5 7\"表示5英尺7英寸:");
int foot;
int inch;
scanf("%d %d",&foot, &inch);
printf("身高是%f米。\n",((foot+inch/12)*0.3048));
  • 错误原因:因为两个整数的运算的结果只能是整数
  • 10和10.0在C中是完全不同的数
  • 10.0是浮点数

二.

  • 带小数点的数值。浮点这个词的本意就是指小数点是浮动的,是计算机内部表达非整数(包含分数和无理数)的一种方式

三.

  • 当浮点数和整数放到一起运算时,C会将整数转换成浮点数,然后进行浮点数的运算

四. double

  • inch是定义为int类型的变量,如果把int换成double,我们就把它改为double类型的浮点数变量了
  • 除了double,还有float(表示单精度浮点数)

五. 数据类型

  • 整数: int, printf("%d"), scanf("%d")
  • 带小数点的数: double, printf("%f"), scanf("%lf")

13. 表达式 🧮

一. 表达式

  • 一个表达式是一系列运算符和算子的组合,用来计算一个值

二. 运算符

  • 运算符是指进行运算的值,这个值可能是常数,也可能是变量,还可能是一个方法的返回值

三. 计算时间差

C

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=t2-t1;
printf("时间差是%d小时%d分",t/60,t%60);

14. 运算符的优先级 🔝

一. 求平均值

  • 写一个程序,输入两个整数,输出他们的平均值

C

int a,b;
scanf("%d %d",&a,&b);
double c=(a+b)/2.0;
printf("%d和%d的平均值=%f\n",a,b,c);

二. 赋值运算符

  • 赋值也是运算,也有结果
  • a=6的结果是a被赋予的值,也就是6
  • a=b=6 -→ a=(b=6)

三. 嵌入式赋值

四. 结合关系

  • 一般自左向右
  • 单目+-和赋值=自右向左

15. 交换两个变量 🔄

一. 程序是按步执行的

  • 程序表达的是顺序执行的动作,而不是关系
  • a=b, b=a 是依次执行的,结果使得a和b都得到b原来的值

二. 交换

C

int t = a;
a=b;
b=t;

16. 复合赋值 🔗

一.

  • 5个算术运算符.+ - * / %,可以和赋值运算符“=”结合起来,形成复合赋值运算符:“+=”、“-=”、“*=”、“/=”、和“%=”
  • total+=5; -> total=total+5;
  • 注意两个运算符中间不要有空格
  • total*=sum+12; -> total=total*(sum+12);

二. 递增递减运算符

  • “++”和“--”是两个很特殊的运算符,他们是单目运算符,这个算子还必须是变量。这两个运算符分别叫做递增和递减运算符,他们的作用就是给这个变量+1或者-1
  • count++;, count+=1;, count=count+1;

三. 前缀后缀

  • ++和--可以放在变量的前面,叫做前缀形式,也可以放在变量的后面,叫做后缀形式
  • a++的值是a加1以前的值,而++a的值是加了1以后的值,无论哪个,a自己的值都加了1

总结:

表达式 运算 表达式的值
count++ 给count加1 count原来的值
++count 给count加1 count+1以后的值
count-- 给count减1 count原来的值
--count 给count减1 count-1以后的值

四. ++ --

  • 这两个运算符有其历史来源
  • 可以单独使用,但是不要组合进表达式

17. 做判断 ⚖️

一. 如果

C

if(条件成立){
...
}

二. 条件

  • 计算两个值之间的关系,所以叫做关系运算
运算符 意义
== 相等
!= 不等于
> 大于
>= 大于或等于
< 小于
<= 小于或等于

三. 关系运算的结果

  • 当两个值的关系复合关系运算符的预期时,关系运算的结果为整数1,否则为整数0(只有这两种值)
  • printf("%d\n",5==3);

四. 优先级

  • 所有的关系运算符的优先级比算术运算的低,但是比赋值运算高
  • 7>=3+4
  • 判断是否相等的==和!=的优先级和其他的低,而连续的关系运算是从左到右进行的

18. 找零计算器 💰

一.

  • 找零计算器需要用户做两个操作:输入购买的金额,输入支付的票面,而找零计算器则根据用户的输入做出相应的动作:计算并打印找零,或告知用户月不足以购买
  • 从计算机程序的角度看,这就是意味着程序需要读用户的两个输入,然后进行一些计算和判断,最后输出结果

二. 找零计算器

C

//初始化
int price =0;
int bill=0;
//输入金额和票面
printf("请输入金额");
scanf("%d",&price);
print("请输入票面");
scanf("%d",&bill);
//计算找零
printf("应该找您:%d\n",bill-price);

三. 注释

  • 以两个斜杠//开头的语句分成了三个部分:1.初始化 2.读入金额和票面 3.计算并打印找零
  • 注释插入在程序代码中,用来向读者提供解释信息。他们对于程序的功能没有任何影响,但是往往能使得程序更容易被人类读者理解
  • /**/注释:延续数行的注释,要用多行注释的格式来写。多行注释由一堆字符序列/*开始,而已*/结束
  • 也可以用于一行内的注释 int ak=47/*36*/,y=9;

四. 判断票面够不够

C

if(bill>=price){
    printf("应该找您:%d\n",bill-price);
}

五. 不够怎么办?

  • else

19. if语句再探 🔍

一.

  • 一个基本的if语句由一个关键字if开头,跟上在括号里的一个表示条件的逻辑表达式,然后是一对大括号“{}”之间的若干条语句。如果表示条件的逻辑表达式的结果不是零,那么就执行后面跟着的这对大括号中的语句,否则就跳过这些语句不执行,而继续下面的其他语句

C

if(total>amount)
    total+=amount+10;
  • if语句这一行结束的时候并没有表示语句结束的“;”,而后的赋值语句写在if的下一行,并且缩进了,在这一行结束的时候有一个表示语句结束的“;”。这表明这套赋值语句是if语句的一部分,if语句拥有和控制这条赋值语句,决定它是否要被执行

20. 嵌套的if-else 🖇️

一. else的匹配

  • else总是和最近的那个if匹配

二. 缩进

  • 缩进格式不能暗示else的匹配

C

if(code==READY)
    if(count<20)
        printf("一切正常\n");
else
    printf("继续等待\n");

三. 嵌套的if

C

if(gameover==0)
    if(player2move==2)
        printf("Your turn\n");

四. tips

  • 在if或else后面总是用{}

21. 级联的if-else 🪜

一. 分段函数

二. if - else if

C

if(expl)
    st1;
else if(exp2)
    st2;
else
    st3;
  • 单一出口,尽量不要把代码写死

22. 多路分支switch-case语句 🛣️

一. switch - case

C

switch(type){
    case 1:
        printf("你好");
        break;
    case 2:
        printf("你好");
        break;
    default:
        break;
}
  • 注意:控制表达式只能是整数型的结果
  • 常量可以是常数,也可以是常数计算的表达式

二. break

  • switch语句可以看做是一种基于计算的跳转,计算控制表达式的值后,程序会跳转到相匹配的case(分支标号)出。分支标号知识说明switch内部位置的路标,在执行完分支中的最后一条语句后,如果后面没有break,就会顺序执行到下面的case里去,直到遇到一个break,或者switch结束位置(case不会阻止语句的执行)

三. 成绩转换

  • 可以想到用十位数来进行switch-case的流程控制

四. try

  • f(x)=-1;x<0
  • 0;x=0
  • 2x;x>0
  • 这个分段函数能否用switch-case?

23. 循环 🔄

一. 数数几位数

  • 程序要读入一个4位以下(含4位)的正整数,然后输出这个整数的位数。
  • 人VS计算机

二. 程序实现

C

int x;
int n=1;
scanf("%d",%x);
if(x>999){
    n=4;
}
else if(x>99){
    n=3;
}
else if(x>9){
    n=2;
}
else{
    n=1;
}
printf("%d",n);
  • 因为题目明确了4位数及以下的正整数,所以可以简化一些判断

三. 换个方式想

  • 352->3很快。1234251235151512341234是几位?数数!
  • 人怎么数?从左往右数,一次划掉一个数字。计算机怎么划掉那个数字?

四. 三位数逆序的题

  • 不断地划,直到没数可以划... 在这个过程中计数

五. 试试代码

C

int x;
int n=0;
scanf("%d",&x);
n++;
x/=10;
while(x>0){
    n++;
    x/=10;
}
printf("%d\n",n);

24. while循环 🔁

一.

  • 如果我们把while翻译作“当”,那么一个while循环的意思就是:当条件满足时,不断地重复循环体内的语句
  • 循环执行之前判断是否继续循环,所以有可能循环一次也没有被执行
  • 条件成立是循环继续的条件

二. 看程序运行结果

  • 人脑模拟计算机的运行,在纸上列出所有的变量,随着程序的进展不断重新计算变量的值。当程序运行结束时,留在表格最下面的就是程序的最终结果

三. 验证

  • 测试程序常使用边界数据,如有效范围两端的数据、特殊的倍数等
  • 个位数; 10; 0; 负数。
  • 在程序适当的地方插入printf来输出变量的内容

25. do-while循环 ↪️

一. 数位数的算法

  1. 用户输入x;
  2. 初始化n为0;
  3. x=x/10,去掉个位
  4. n++;
  5. 如果x>0,回到3;
  6. 否则n就是结果

二. do-while循环

  • 在进入循环的时候不做检查,而是在执行完一轮循环体的代码之后,再来检查循环的条件是否满足,如果满足则继续下一轮循环,不满足则结束循环

C

do{
    <循环体语句>
}while(<循环条件>);

三. 两种循环

  • do-whlie循环和while循环很像,区别是在循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时结束循环

26. 猜数游戏 🎲

一. 思路

  1. 让计算机来想一个数,然后让用户来猜,用户每输入一个数,就告诉它是大了还是小了,直到用户猜中为止,最后还要告诉用户它采了多少次
  2. 因为需要不断重复让用户猜,所以需要用到循环
  3. 核心重点是循环的条件

二. 步骤

  1. 计算机随机想一个数,记在变量number里
  2. 一个负责计次数count初始化为0
  3. 让用户输入一个数字a;
  4. count递增(加一)
  5. 判断a和count的大小关系,如果a大,就输出“大”,如果a小就输出“小”
  6. 如果a和number是不相等的(无论大还是小),程序转回到第3步
  7. 否则,程序输出“猜中”和次数,然后结束

三. 随机数

  • 每次召唤rand()就得到一个随机的整数

C

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main(){
    srand(time(0));
    int a=rand();
    printf("%d\n",a);
    return 0;
}

四. %100

  • x%n的结果是[0,n-1]的一个整数

五. 完整代码示例

C

srand(time(0));
int number =rand()%100+1;
int count =0;
int a = 0;
printf("我已经想好了一个1到100之间的数。");
do{
    printd("请猜这个1到100之间的数:");
    scanf("%d",&a);
    count ++;
    if (a > number){
        printf("你猜的数大了");
    }
    else if (a < number){
        printf("你猜的数小了");
    }
}while (a!=number);
printf("太好了,你用了%d次就猜到了答案。\n",count);

27. 算平均数 📏

一.

  • 让用户输入一系列的正整数,最后输入-1表示输入结束,然后程序计算出这些数字的平均数,输出输入的数字的个数和平均数
  • 变量->算法->流程图->程序

二. 变量

  • 一个记录读到的整数的变量
  • 平均数要怎么算?只需要每读到一个数,就把它加到一个累加的变量里,到全部数据读完,再拿它去除读到的数的个数就可以了
  • 一个变量记录累加的结果,一个变量记录读到的数的个数

三. 算法与示例

C

#include<stdio.h>
int main(){
    int number;
    int sum=0;
    int count=0;
    scanf("%d",&number);
    while(number!=-1){
        sum+=number;
        count++;
        scanf("%d",&number);
    }
    printf("%f\n",1.0*sum/count);
    return 0;
}

28. 整数求逆 ↩️

一. 整数的分解

  • 对一个整数做%10的操作,就得到它的个位数
  • 对一个整数做/10的操作,就去掉了它的个位数
  • 然后再对2的结果做%10,就得到原来数的十位数了

二. 数的逆序

C

#include<stdio.h>
int main(){
    int x;
//  scanf("%d",&x);
    x=700;
    int digit;
    int ret=0;
    while(x>0){
        digit=x%10;
//      printf("%d",digit);
        ret=ret*10+digit;
        printf("x=%d,digit=%d,ret=%d\n",x,digit,ret);
        x/=10;
    }
    printf("%d",ret);
    return 0;
}

29. for 循环 🔢

一. 阶乘

  • n!=1x2x3x...xn

C

int n;
scanf("%d",&n);
int fact=1;
int i =1;
for (i=1;i<=n;i++){
    fact*=i;
}
printf("%d!=%d\n",n,fact);

二. for循环

  • for循环像一个计数循环:设定一个计数器,初始化它,然后在计数器到某值之前,重复执行循环体,而每执行一轮循环,计数器值以一定步骤b变化,比如加1或者减1

C

for(i=0;i<5;i=i+1){
    printf("%d",i);
}

三. for = 对于

  • for(count=10;count>0;count--) 就读成:“对于一开始的count=10,当count>0时,重复做循环体,每一轮循环在经过循环体内的语句后,使得count--”

四. 小套路

  • 做求和的程序时,记录结果的变量应该初始化为0,而做求积的变量时,记录结果的变量应该初始化为1
  • 循环控制变量i只在循环里被使用了,循环外面它没有任何用处。因此,我们可以把变量i的定义写到for语句里面去

五. try

  • 除了可以从1乘到n来计算n!,还可以从n乘到1来计算吧?试试把循环换个方向来计算n。这时候,还需要循环控制变量i吗?

C

int n;
scanf("%d",&n);
int fact=1;
int i=1;
for (i=n;n>1;n--){
    fact*=n;
}
printf("%d!==%d\n",i,fact);

30. 循环的计算和选择 ⚙️

一. 循环次数

  • for(i=0;i<n;i++) 则循环的次数是n,而循环结束以后,i的值是n

二. for循环

  • for(初始动作;条件;每轮的动作){}
  • for中的每一个表达式都是可以省略的
  • for(;条件;)==while(条件)

三. tips for loops

  • 如果有固定次数,用for
  • 如果必须执行一次,用do_while
  • 其他情况用while

31. 循环控制:break和continue 🛑

一. 素数

  • 只能被1和自己整除的数,不包括1

C

#include<stdio.h>
int main(){
    int x;
    scanf("%d",&x);
    int i;
    int isPrime=1;  //x是素数
    for (i=2;i<x;i++){
        if(x%i==0){
            isPrime=0;
            break;
        }
    }
    if (isPrime==1){
        printf("是素数\n");
    }
    else{
        printf("不是素数\n");
    }
    return 0;
}

二. break vs continue

  • break:跳出循环
  • continue: 跳过循环这一轮剩下的语句进入下一轮

32. 循环嵌套 🪆

一. 100以内的素数

  • 如何写程序输出100以内的素数?

二. 嵌套的循环

  • 循环里面还是循环

三. 凑硬币

  • 如何用1角、2角和5角的硬币凑出10元以下的金额呢?
  • break和continue只能对它所在的那层循环做
  • 跳出所有循环可以使用接力break

C

int x;
int one,two,five;
int exit=0;
scanf("%d",&x);
for (one=1;one <x*10;one++){
    for (two=1;two<x*10/2;two++){
        for (five=1;five<x*10/5;five++){
            if (one+two*2+five*5==x*10){
                printf("可以用%d个1角加%d个2角加%d个5角得到%d元\n",one,two,five,x);
                exit=1;
                break;
            }
        }
        if(exit==1)break;
    }
    if(exit==1)break;
}

四. goto

  • 最好在最内层跳到外层时才用,不然最好不要用(破坏代码的结构性)

33. 前n项求和 ➕

一.

C

int n;
int i;
double sum=0.0;
scanf("%d",&n);
for (i=1;i<=n;i++){
    sum+=1.0/i;
}
printf("f(%d)=%f\n",n,sum);

二. 加减交替

  • sign=-sign;

34. 正序分解整数 🔢

一.

  • 输入一个非负整数,正序输出它的每一位数字。输入:13425,输出:1 3 4 2 5

二. 格式化

  • 怎么解决最后一个数据后面有带空格呢?使用if判断

三.

  • 1.先逆序再正序
  • 2.使用求余和整除的方法
  • 3.改进的代码 (处理末尾为0的数字)

35. 求最大公约数 ➗

一.

  • 输入两个数a和b,输出他们的最大公约数

二. 枚举

  • 设t为2;如果u和v都能被t整除,则记下这个t;t加1后重复,直到t等于u或v

三. 辗转相除法

  1. 如果b等于0,计算结束,a就是最大公约数;
  2. 否则,计算a除以b的余数,让a等于b,而b等于那个余数;
  3. 回到第一步

C

int a,b;
int t;
scanf("%d %d",&a,&b);
while(b!=0){
    t=a%b;
    a=b;
    b=t;
}
printf("gcd=%d\n",a);

46. 编程练习解析 📝

一. 求复合给定条件的整数集

  • 给定不超过6的正整数A,考虑从A开始的连续4个数字,请输出所有由他们组成的无重复数字的3位数

二. 水仙花数(20)

  • 水仙花数是指一个N位正整数(N>=3),它的每个位上的数字的N次幂之和等于它本身。

三. 打印九九口诀表

  • 输出下三角N*N部分口诀表

四. 统计素数并求和

  • 统计给定整数M和N区间内素数的个数并对他们求和

五. 猜数字游戏

  • 自动对其与随机产生的被猜数进行比较,并提示大了还是小了,根据猜测次数输出不同提示

六. 求序列前N项和

  • 计算序列2/1+3/2+5/3+8/5+...的前N项之和

七. 约分最简分式

  • 要求用户输入一个分数,然后将其约分为最简分式

八. 念数字

  • 输入一个整数,输出每个数字对应的拼音

九. 求a的连续和

  • 求数列之和S=a+aa+aaa+...+aaa...a(n个a)

47. 数据类型 🗂️

一. C是有类型的语言

  • C语言的变量,必须:在使用之前定义,并且确定类型

二. 类型安全

  • 早期语言强调类型,面向底层的语言强调类型
  • C语言需要类型,但是对类型的安全检查并不足够

三. C语言的类型

  • 整数: char、short、int、long、long long
  • 浮点数: float、double、long double
  • 逻辑: bool
  • 指针
  • 自定义类型

四. 类型有何不同

  • 类型名称、输入输出格式、表达范围、占据大小、内存表达形式

五. sizeof

  • 是一个运算符,给出某个类型或边浪在内存中所占据的字节数
  • sizeof(int)
  • 是静态运算符,它的结果在编译时刻就决定了

48. 整数类型 📏

一.

  • short是2个字节
  • long是8个字节
  • long long也是8个字节

二.

  • char: 1字节(8比特(bit))
  • short: 2字节
  • int: 取决于编译器(CPU),通常的意义是“1个字”

49. 整数的内部表达 🤖

一.

  • 计算机内部一切都是二进制

二. 如何表示负数

  • 十进制用'-'来表示负数,在做计算的时候

三. 二进制负数

  • 补码: 考虑-1,我们希望-1+1->0。
  • 11111111被当作纯二进制看待时,是255,被当作补码看待时是-1
  • 补码的意义就是拿补码和原码可以加出一个溢出的“零”

50. 整数的范围 📉

一. 数的范围

  • 对于一个字节(8位),可以表达的是: 00000000-11111111

二.

  • char: 1字节:-128~-127
  • short: 2字节:-32768~32767

三. unsigned

  • 如果一个自变量常熟想要表达自己是unsigned,可以在后面加u或U
  • unsigned的初衷并非扩展数能表达的范围,而是为了做纯二进制运算,主要是为了移位

四. 整数越界

  • 整数是以纯二进制方式进行计算的
  • 11111111+1->100000000->0

50. 整数的格式化输出 🖨️

一. 整数的输入输出

  • 只有两种形式:int或long long
  • %d: int, %u: unsigned, %ld: long long, %lu: unsigned long long

二. 8进制和16进制

  • 一个以0开始的数字字面量是8进制, %o
  • 一个以0x开始的数字字面量是16进制, %x

51. 浮点类型 ☁️

一.

类型 字长 范围 有效数字
float 32 7
double 64 15

二. 浮点的输入输出

类型 scanf printf
float %f %f,%e
double %lf %f,%e

52. 浮点的范围与精度 🎯

一. 超过范围的浮点数

  • inf: 无穷
  • nan: 不存在的浮点数

四. 选择浮点类型

  • 如果没有特殊需要,只使用double

53. 字符类型 🅰️

一. 字符类型

  • char是一种整数,也是一种特殊的类型:字符
  • printf和scanf里用%c来输入输出字符

二. 字符的输入输出

  • '1'的ASCII编码是49

四. 字符计算

  • 一个字符加一个数字得到ASCII码表中那个数之后的字符
  • 两个字符的减,得到他们在表中的距离

五. 大写字母和小写字母

  • a+'A'-'a'可以把一个大写字母变成小写字母

54. 逃逸字符 🏃

一. 逃逸字符

  • 用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠\开头
字符 意义 字符 意义
\b 回退一格 \" 双引号
\t 到下一个制表位 \' 单引号
\n 换行 \\ 反斜杠本身
\r 回车

55. 类型转换 🔄

一. 自动类型转换

  • 当运算符的两边出现不一致的类型时,会自动转换成较大的类型
  • char->short->int->long->long long
  • int->float->double

二. 强制类型转换

  • (类型)值 比如 (int)10.2
  • 强制类型转换的优先级高于四则运算

56. 逻辑类型 ✅

一. bool

  • #include<stdbool.h> 之后就可以使用bool和true、false

57. 逻辑运算 🧠

一.

运算符 描述 示例 结果
! 逻辑非 !a 如果a是true结果就是false
&& 逻辑与 a&&b 如果a和b都是true,结果就是true
| | 逻辑或

四. 短路

  • 逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算

58. 条件运算和逗号运算 ❓

一. 条件运算符

  • count=(count>20)?count-10:count+10

四. 逗号运算

  • 逗号用来连接两个表达式,并以其右边的表达式的值作为它的结果。
  • i=3+4,5+6 -> i=(3+4,5+6)

59. 初见函数 📦function call stack frame的图片Shutterstock探索

一. 素数求和

C

int isPrime(int i){
    int ret=1;
    // ... logic ...
    return ret;
}

二. 求和

  • “代码复制”是程序质量不良的表现

60. 函数的定义和使用 🔧

一. 什么是函数?

  • 函数是一块代码,接收零个或多个参数,做一件事情,并返回零个或一个值
  • 函数头:**返回类型** 函数名(参数表)

二. 调用函数

  • 函数名(参数值)
  • ()起到了表示函数调用的重要作用

61. 从函数中返回 🔙

一. 从函数中返回值

  • return停止函数的执行,并送回一个值

二. 没有返回值的函数

  • void 函数名(参数表)

62. 函数原型 🏗️

三. 函数原型

  • 函数头,以分号“;”结尾,就构成了函数的原型
  • 函数原型的目的是告诉编译器这个函数长什么样

63. 参数传递 📨

三. 传过去的是什么?

  • C语言在调用函数时,永远只能传值给函数

四. 传值

  • 每个函数有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系

64. 本地变量 🏠

一. 本地变量

  • 函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量

二. 变量的生存期和作用域

  • 生存期:什么时候这个变量开始出现了,到什么时候它消亡了
  • 作用域:在(代码的)什么范围内可以访问这个变量

65. 函数庶事 📝

一. 没有参数时

  • void f(void);

三. 函数里的函数?

  • C语言不允许函数嵌套定义

66. 初试数组 📚

三. 数组

  • int number[100];
  • 1.定义数组 2.对数组中的元素赋值 3.使用数组中的元素 4.遍历数组

67. 定义数组 📏

一. 定义数组

  • <类型> 变量名称[元素数量];
  • 元素数量必须是整数

二. 数组

  • 是一种容器(放东西的东西),特点是:其中所有的元素具有相同的数据类型;一旦创建,不能改变大小;数组中的元素在内存中是连续依次排列的

四. 数组的单元

  • 使用数组时放在[]中的数字叫做下标或索引,下标从0开始计数

五. 有效的下标范围

  • 编译器和运行环境都不会检查数组下标是否越界
  • 一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃 segmentation fault

68. 数组的例子:统计个数 📊

  • 写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数

69. 数组运算 🧮

二. 数组的集成初始化

  • int a[]={2,4,5234,12,4};

三. 集成初始化时的定位

  • int a[10]={[0]=2,[2]=3,6,};

四. 数组的大小

  • sizeof(a)/sizeof(a[0])

五. 数组的赋值

  • 数组变量本身不能被赋值,要把一个数组的所有元素交给另一个数组,必须采用遍历

70. 数组例子-素数 🔢

一.

  • 从2到x-1测试是否可以整除

二. 构造素数表

  • 欲构造n以内的素数表

71. 二维数组 ⬛

一. 二维数组

  • int a[3][5];

二. 二维数组的遍历

C

for(i=0;i<3;i++){
    for(j=0;j<5;j++){
        a[i][j]=i*j;
    }
}

三. 二维数组的初始化

  • 列数是必须给出的,行数可以由编译器来数

四. tic-tac-toe游戏

72. 取地址运算 📍

一. 运算符&

  • 获得变量的地址,它的操作数必须是变量
  • int i; printf("%p",&i);

73. 指针 👆

二. 指针

  • 就是保存地址的变量
  • int* p =&i;

四. 访问那个地址上的变量*

  • *是一个单目运算符,用来访问指针的值所表示的地址上的变量
  • int k=*p;

74. 指针的使用 🛠️

一. 指针应用场景一

  • 交换两个变量的值

二. 指针应用场景二

  • 函数返回多个值,某些值就只能通过指针返回
  • 函数返回运算的状态,结果通过指针返回

四. 指针最常见的错误

  • 定义了指针变量,还没指向任何变量,就开始使用指针

75. 指针与数组 🔗

一. 传入函数的数组成了什么?

  • 函数参数表中的数组实际上是指针
  • sizeof(a)==sizeof(int*)

三. 数组变量是特殊的指针

  • 数组变量本身表达地址
  • int a[10];int*p=a;//无需用&取地址
  • a==&a[0]

76. 指针与const 🔒

二. 指针是const

  • 表示一旦得到了某个变量地址,不能再指向其他变量
  • int*const q=&i;

三. 所指的是const

  • 表示不能通过这个指针去修改那个变量(并不能使得那个变量称为const)
  • const int*p=&i;

77. 指针运算 ➕

一. 1+1=2?

  • 给一个指针+1表示要让指针指向下一个变量
  • 如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义

二. 指针计算

  • 给指针加、减一个整数(+,+=,-,-=)
  • 两个指针相减(表示之间有几个东西(/sizeof))

五. 0地址

  • NULL是一个预定义的符号,表示0地址

78. 动态内存分配 🧠

二. malloc

  • #include<stdlib.h>
  • void*malloc(size_t size)
  • 向malloc申请的空间的大小是以字节为单位的

四. free()

  • 把申请得来的空间还给“系统”
  • 只能还申请来的空间的首地址

79. 字符串 🧵

二. 字符串

  • 以0(整数0)结尾的遗传字符
  • 0标志字符串的结束,但它不是字符串的一部分

80. 字符串变量 🔡

一. 字符串常量

  • char*s="Hello,world!";
  • s是一个指针,初始化为指向一个字符串常量
  • 如果需要修改字符串,应该用数组; char s[]="Hello,world!";

81. 字符串的输入和输出 ⌨️

一. 字符串赋值?

  • 并没有产生新的字符串,只是让指针s指向了t所指的字符串

三. 安全的输入

  • scanf("%7s",string);
  • 在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小一

82. 字符串数组 📚

一. 字符串数组

  • char **a;

83. 单字符输入输出 🔤

一. putchar

  • int putchar(int c);

二. getchar

  • int getchar(void);

84. 字符串函数 🛠️

一. string.h

  • strlen, strcmp, strcpy, strcat, strchr, strstr

二. strlen

  • size_t strlen(const char*s); 返回s的字符串长度(不包括结尾的0)

三. strcmp

  • 比较两个字符串

四. strcpy

  • char * strcpy(char *restrict dst,const char *restrict src);

85. 枚举 🔢

三. 枚举

  • 枚举是一种用户定义的数据类型
  • enum 枚举类型名字{名字0,...,名字n}

86. 复杂数据类型 🏗️

一. 用户自己建立数据类型

  • 结构体:是一种构造数据类型
  • 关键字:struct

二. 定义结构体类型变量

  1. 先声明结构体类型,再定义结构体变量
  2. 声明结构体类型的同时定义结构体变量

三. 结构体变量的引用和初始化

  • 结构体变量名.成员名

五. 指向结构体类型数据的指针

  • 存放结构体首地址
  • p->成员名

87. 文件 📁

一. 文件的基本概念和文件指针

  • 文本文件:占存储空间多,需要转换时间,但方便字符处理
  • 文件指针: FILE *fp;

二. 文件的打开和关闭

  • fp=fopen("a1",“r”);
  • fclose(文件指针);

三. 文本文件读写

  • fgetc(), fputc()
  • fgets(), fputs()
  • fprintf(), fscanf()

四. 二进制文件读写

  • fread(buffer,size,count,fp);
  • fwrite(buffer,size,count,fp);