自学编程|【浙大翁恺C语言】从0入门笔记【国家精品课程】(上)

C语言程序设计 所有代码都展示main函数里面的代码
计算机的思维
辗转相除法

int u = 32; int v = 32; //如果v=0,计算结束,u就是最大公约数 //v!=0计算u/v的余数,让u=v v=余数 while(v!+0) { int temp = u%v; u = v; v = temp; } printf("%d",u);

【自学编程|【浙大翁恺C语言】从0入门笔记【国家精品课程】(上)】从计算机到程序再到算法
计算机的思维和优势是把所有可能列出来挨个试(枚举)
用二分法可以进一步简化运算提高算法效率
程序的执行
解释:借助一个程序,那个程序能试图理解你的程序,然后按照你的要求执行
编译:借助一个程序,那个程序能将你的程序翻译成机器语言后执行
编译和解释的区别与看法
  • 语言本无编译和解释之分
  • 一个语言使用编译还是解释更多是取决于常用的执行方式是什么
  • 解释型语言有特殊的计算能力
  • 编译型语言有确定的运算性能
案例:算找零
需求:
  • 有地方放输入的数字
  • 有办法输入数字
  • 输入的数字参加运算
int price = 0; printf("请输入金额(元)"); scanf("%d",&price); int change = 100 - price; printf("找您%d元。\n",change);

优化
  • 便于修改维护
  • 减少不明意义的数字
int price = 0; const int AMOUNT = 100; printf("请输入金额(元)"); scanf("%d",&price); int change = AMOUNT - price; printf("找您%d元。\n",change);

const const是个修饰符,加在int前面,const修饰变量一经初始化则无法修改,还可以进行再次优化,让用户输入那个变量的值,而不是固定的100
变量
变量定义的一般形式:<类型名称><变量名称>;
在数学中a=b和b=a意义完全相同,但是在程序设计中意义完全相反
所有变量在第一次使用时之前应被赋值(初始化)
<类型名称><变量名称>=<初始值>;
关于scanf
scanf("%d",&price);

要求scanf这个函数去读入下一个整数,读到的结果赋值给price
双引号里的时格式字符串,%d表示读取整数
小心price前面的&,在scanf变量前要加&
案例:计算时间差
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); //利用t/60抽象出小时,t%60抽象出分钟

案例:求俩个整数的平均值
int a,b; scanf("%d %d",&a,&b); //人们利用浮点数表达所有的带小数点的数,当浮点数和整数放到一起运算的时候,c会将整数转换为浮点数,然后进行浮点运算 double c = (a+b)/2.0; printf("%d和%d的平均值为%lf",a,b,c);

数据类型 中文名称 格式字符串
double 双精度浮点数 “%lf”
float 单精度浮点数 “%f”
int 整数 “%d”
案例:交换俩个变量
int a = 5; int b = 6; int t = 0; t = a; a = b; b = t; printf("a=%d b=%d",a,b);

复合赋值
+= -= *= /= %= ++ –
这些符号是因为c语言继承了曾经的机器语言
count ++; count += 1; count = count + 1;

上述三种是一个意思都能完成加一
a++表示加一以前的值
++a表示加一以后的值
但这俩种运算过后,下一行再用a都是加一过后的值
判断运算
/*if(条件成立){ ...; }*/ // ==相等 !=不等 >大于 >=大于或者等于 <小于 <=小于或等于 //当两个值的关系符合关系运算符的预期时,关系运算的结果为1,否则为0 printf("%d\n",5==3); printf("%d\n",5>3); printf("%d\n",5<3); //所有的关系运算符的优先级比算术运算的低,但是比赋值运算的高 7 >= 3+4; int r = a>0; //判断是否相等的优先级比其他的低 //连续的关系运算是从左到右进行的

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

但是我们上面那个程序无法判断给够钱了嘛,即判断票面够不够
我们进行一个逻辑判断对程序进行优化
//初始化 int price = 0; int bill = 0; //读入数据 printf("请输入金额"); scanf("%d",&price); printf("请输入票面"); scanf("%d",&bill); if(bill>=price) { printf("应该找您:%d\n",bill-price); } else { printf("钱不够还差:%d\n",price-bill); }

案例:比较数的大小
案例1:两个数的比大小
int a,b; printf("请输入两个正整数:"); scanf("%d %d",&a,&b); int max = 0; if(a>b) { max = a; } else {max = b; } printf("%d和%d的最大值是%d",a,b,max);

案例2:三个数的比大小(if嵌套方法)
inta,b,c; scanf("%d %d %d",&a,&b,&c); int max = 0; if(a>b) { if(a>c) {max = a; } else {max = c; } } else { if(b>c) {max = b; } else {max = c; } } printf("最大值是%d",max);

案例二也可以用别的方法解决,如冒泡排序等
编程习惯tips
  • else总是和离它最近的if匹配,所以日常建议写一个if就写一个else
  • 缩进不能暗示else的匹配!!! 缩进不能暗示else的匹配!!! 缩进不能暗示else的匹配!!!
  • if后面只有单行语句的时候可以不用花括号,但是为了整体风格协调好阅读建议都加花括号
案例:分段函数的表达(if-else if-else)
有如下分段函数,要求设计一个程序,输入x输出y
f(x) = -1 x<0
f(x) = 0 x=0
f(x) = 2x x>0
//只展示if部分 if(x<0) {f = -1; } else if(x=0) {f = 0; } else {f = 2*x; }

编程习惯tips
  • 缩进时注意else对齐(级联结构)
  • 代码编写尽量单一出口,多封装减少出口(如上文f)
if语句常见错误
  • 忘加大括号
  • if后加入了分号
  • 错用==
  • else不规范
switch结构
若级联过多,执行效率会非常低,可用switch结构
switch(控制表达式){ case 常量:语句;break; case 常量:语句;break; default:语句break; }

控制表达式只能是整数类型的结果
常量表达式可以是常数,也可以是表达式
switch语句可以看作一种基于计算的跳转,多个值对应一个语句
案例:成绩转换
成绩大于等于90为A
成绩大于等于80为B
成绩大于等于70为C
成绩大于等于60为D
成绩小于60为E
int grade; scanf("%d",&grade); grade /= 10; //我们只抽象出十位数进行运算,这样就可以使用switch结构 switch(grade) {case 10: case 9: printf("A\N"); break; case 8: printf("B\N"); break; case 7: printf("C\N"); break; case 6: printf("D\N"); break; default: printf("E\N"); break; }

级联>的时候从高往下判断
级联<的时候从下往高判断
案例:数数几位数(while循环)
int x; int n = 0; scanf("%d",&x); //这行代码是保证输入0的时候输出位数是1 n++; x /= 10; while(x>0) {n++; x /= 10; } printf("%d\n",n);

tips
  • 循环体一定要有改变循环条件的机会
  • 打断点自己测试程序的时候,测试数据常用的有边界数据,有效范围,特殊的倍数
  • 在程序中适当加入注释以及printf进行测试可以提升程序可行性和可读性
优化
我们现实中很多情况都需要先执行一次再判断循环
do-while会先执行一次再判断循环
int x; int n = 0; scanf("%d",&x); do {n++; x /= 10; } //这个while后面一定要打分号 while(x>0); printf("%d\n",n);

拓展案例:计数循环
int count = 100; while(count>=0) {//这两行决定第一个输出和最后一个输出的数据是什么 count--; printf("%d\n",count); } printf("发射\n");

案例:猜数循环
计算机想一个数,然后让用户去猜
猜的时候提醒大了还是小了直到猜中为止
补充知识
  • 每次召唤rand()就能得到一个随机的整数
  • x%n的结果是[0,n-1]的整数
  • x%100是表示x对100取余也就是只剩下十位个位
程序设计思路
  • 储存用户输入的数并且设置不猜中就不能出去的循环
  • 判断大了还是小了
  • 创建变量储存到底输入了多少次
  • 优化
srand(time(0)); int number = rand()%100+1; int count = 0; int a = 0; printf("我已经想好了1-100的整数"); do{printf("请输入这个1-100的整数"); scanf("%d",&a); count++; if(a>number) {printf("大了"); } else {printf("小了"); } } while(a!=number); prinf("用了%d次猜中了数字%d",count,number);

案例:算平均数
让用户输入一系列正整数,最后输入-1结束,然后程序计算出这些数字的平均数和数字个数
程序设计思路
  • 用循环让用户持续输入,并且等于-1时不进行循环
  • 创建变量存储数字的个数
  • 利用1.0的妙用强制转换数据类型为浮点型
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);

案例:整数的分解
输入一个正整数(int范围内的整数),输出逆序的数
程序设计思路
  • 对一个整数%10得到个位,对一个整数/10%10得到十位以此类推得到每一位
  • 逆序处理
  • 对结尾是0的进行处理
int x = 0; int sum = 0; int t = 0; scanf("%d",&x); int count = 0; //防止x改变利用一个变量代替x进行操作 t = x; //我们先得到x是几位数,为了防止输入的是0,我们要先进行一次运算 t /= 10; count++; while(t!=0) {t /= 10; count++; } //逆序处理 //有几位数就进行几次运算 for(int i=0; i0; j--) {a *= 10; } //计算和即可 sum += a; } printf("%d",sum);

for循环总结
案例1:算阶乘
int n; scanf("%d",&n); int fact = 1; int i = 1; for(i=1; i<=n; i++) { fact *= i; } printf("%d",fact);

案例2:是否是素数
int x; scanf("%d",x); int i; int isPrime = 1; for(i=2; i

关于for循环
  • for循环像一个计数循环:初始化——循环条件——重复执行并调整
  • for中的每一个表达式都是可以省略的
  • for(; 条件; )=while(条件)
tips
  • 如果有固定次数用for
  • 必须执行一次用do_while
  • 其余用while
案例:凑硬币(如何从嵌套循环中脱出)
如何用1.2.5角凑10元以下的金额
方案1:接力break
int x; int one,two,five; int exit = 0; scanf("%d",&x); for(one=1; one

方案2:goto
int x; int one,two,five; int exit = 0; scanf("%d",&x); for(one=1; one

案例:正序分解整数
输入一个非负整数,正序输出每一位,每一位中间有空格,最后无空格
程序设计思路
  • 先判断有几位数字
  • 进行抽象每位正序数,并且间隔空格
  • 处理最后一位不空格
int x; scanf("%d",&x); int mask = 1; while(t>9) {t /= 10; mask *= 10; } do { int d = x / mask; printf("%d",d); if(mask>9) {printf(" "); } x %= mask; mask /= 10; } while(mask>0); printf("\n");

案例:最大公约数(枚举与辗转相除)
方法一:设t为2,如果u,v都能被整除则记下t,t++后重复第二步直到等于u和v的最小值,那么曾经记下的最大的t就是goc
int a,b; int min=0; scanf("%d %d",&a,&b); if(a>b) { max = a; } else { max = b; } int ret = 0; for(int i=0; i<=min; i++) { if(a%i==0) {if(b%i==0) {ret == i; } } } printf("%d",ret);

方法二:辗转相除法
int u = 32; int v = 32; //如果v=0,计算结束,u就是最大公约数 //v!=0计算u/v的余数,让u=v v=余数 while(v!+0) { int temp = u%v; u = v; v = temp; } printf("%d",u);

C的数据类型以及语言特性
C是有类型的语言,在使用前必须定义并且确定类型
C以后的语言向俩个方向发展
  • C++/Java更加强调类型,对类型的检查更加严格
  • Javascrip,Python,PHP不看重类型,甚至不需要定义
C语言的类型
  1. 整数
    • char
    • short
    • int
    • long
    • long long
  2. 浮点数
    • float
    • double
    • long double
  3. 逻辑
    • bool
  4. 指针
  5. 自定义类型
sizeof
sizeof是一个运算符,算出表达式在内存中占几个字节
注意
  • sizeof是静态运算符,他的运算符在编译的时候就已经注定
  • 不要在sizeof里的括号作运算
整数的内部表达
计算机内部一切都是二进制
那我们如何表达负数呢?
在十进制的时候,我们负号其实是独立于数字单独运算
那么二进制负数有三种方案
  1. 仿照十进制,用一个特殊标志
  2. 取中间数字为0,比它大是整数比它小是负数
  3. 补码
但是前两种对于人类来说好理解,但是在计算机设计上比较复杂
利用位数上限进行设计
0->00000000
1->00000001
-1->11111111
全一被当作纯二进制看待时,是255,被当作补码看待时是-1
补码的意义就是拿补码和原码可以加出来一个溢出来的0
整数的范围
char c = 255; int i = 255; printf("c=%d,i=%d",c,i);

输出结果c的值为-1,i则为255
所以不同类型有不同的范围
负的数字范围会比整数多一个
unsigned
如果我们不想用补码的形式表示,我们可以使用unsigned关键字,让变量表达为纯二进制
正数范围扩大,负数不再表达
unsigned的初衷并非扩展数能表达的范围,而是为了去做纯二进制运算,为了进行移位
案例:计算int类型的最大数
利用原理:超过上限会溢出变成负的最大值,减一以后刚好回归正数最大值
int a = 0; int b = 0; while(++a>0); printf("int数据类型最大数是:%d\n",a-1); b++; while((a=a/10)!=0) {b++; } printf("int数据类型组最大数的位数是:%d",b);

整数的格式化
整数的输入输出只有两种形式:int或者long long
一个数字以0开头就是8进制
一个数字以0x开头就是16进制
%0用于8进制
%x用于16进制
计算机内部永远只有二进制,你写的八进制编译的时候会进行转换
8进制和16进制只是如何把数字表达成字符串,与内部如何表达数字无关
16进制一位刚好是4位2进制的数
选择整数类型
整数为什么要有那么多种,是为了准确表达内存,做底层程序的需要
但是一般来说,没有特殊需要就选择int
  • 现在的cpu的字长普遍是32或者64位,一次内存读写和计算都是一个int,选择更短的类型不见得会更快,反而有可能更慢
  • 现在编译器会设计内存对齐,所以更短的类型实际也会占据一个int大小,虽然sizeof说比int小
unsigned与否只是输出不同,内部计算是一样的
浮点类型
double与float
float有效数字为7位
double有效数字为15位
总有一块靠近0但不等于0的数字我们无法表达,即靠近0的无穷小无法表达
科学计数法用E表示
输出精度
在%和f之间加上.n可以指定输出小数点后几位,这样的输出是可以做四舍五入的
printf("%.3f\n",-0.0049); printf("%.30f\n",-0.0049); printf("%.3f\n",-0.00049);

    推荐阅读