1.计算机与编程语言
一. 程序是用特殊的编程语言表达了如何解决问题的 不是用编程语言来和计算机交谈,而是描述要求它如何做事情的过程或方法 二.算法 我们要让计算机做计算,就需要像这样找出计算的步骤,然后用编程语言写出来 计算机做的所有的事情都叫做计算 计算的步骤就是算法
2.计算机的思维方式
一.程序的执行 解释:借助一个程序,那个程序能试图理解你的程序,然后按照你的要求执行 编译:借助一个程序,就像一个翻译,把你的程序翻译成计算机真正能懂的语言--机器语言--写的程序,然后这个机器语言写的传给你就能直接执行了
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程序
一. #includeint main(){ printf("Hello World!\n"); return 0; } 二.程序的主干 #include 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
一. #includeint main(){ int a=0,b=0; scanf("%d,%d",&a,&b); printf("%d,%d",a,b); return 0; } 结果为a,0(如果没有按照双引号内的格式输入,scanf会出错)(scanf的引号里面是用户一定要输入的东西)
12.浮点数
一. 计算身高的程序 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.表达式
一.表达式 一个表达式是一系列运算符和算子的组合,用来计算一个值 二.运算符 运算符是指进行运算的值,这个值可能是常数,也可能是变量,还可能是一个方法的返回值 三.计算时间差 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.运算符的优先级
一.求平均值 写一个程序,输入两个整数,输出他们的平均值 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原来的值 二.交换 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以后的值 四.++-- 这两个运算符有其历史来源 可以单独使用,但是不要组合进表达式 ++j++ __>? i++++ __>? a=b+=c++-d+--e/-f
17.做判断
一.如果 if(条件成立){ ... } 二.条件 计算两个值之间的关系,所以叫做关系运算 运算符 意义 == 相等 != 不等于 > 大于 >= 大于或等于 < 小于 <= 小于或等于 三.关系运算的结果 当两个值的关系复合关系运算符的预期时,关系运算的结果为整数1,否则为整数0(只有这两种值) printf("%d\n",5==3); printf("%d\n",5>3); pirntf("%d\n",5<=3); 四.优先级 所有的关系运算符的优先级比算术运算的低,但是比赋值运算高 7>=3+4 判断是否相等的==和!=的优先级和其他的低,而连续的关系运算是从左到右进行的
18.找零计算器
一. 找零计算器需要用户做两个操作:输入购买的金额,输入支付的票面,而找零计算器则根据用户的输入做出相应的动作:计算并打印找零,或告知用户月不足以购买 从计算机程序的角度看,这就是意味着程序需要读用户的两个输入,然后进行一些计算和判断,最后输出结果 二.找零计算器 //初始化 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; 四.判断票面够不够 if(bill>=price){ printf("应该找您:%d\n",bill-price); } 五.不够怎么办? else
19.if语句再探
一. 一个基本的if语句由一个关键字if开头,跟上在括号里的一个表示条件的逻辑表达式,然后是一对大括号“{}”之间的若干条语句。如果表示条件的逻辑表达式的结果不是零,那么就执行后面跟着的这对大括号中的语句,否则就跳过这些语句不执行,而继续下面的其他语句 if(total>amount) total+=amount+10; if语句这一行结束的时候并没有表示语句结束的“;”,而后的赋值语句写在if的下一行,并且缩进了,在这一行结束的时候有一个表示语句结束的“;”。这表明这套赋值语句是if语句的一部分,if语句拥有和控制这条赋值语句,决定它是否要被执行
20.嵌套的if-else
一.else的匹配 else总是和最近的那个if匹配 二.缩进 缩进格式不能暗示else的匹配 if(code==READY) if(count<20) printf("一切正常\n"); else printf("继续等待\n"); 三.嵌套的if if(gameover==0) if(player2move==2) printf("Your turn\n"); 四.tips 在if或else后面总是用{}
21.级联的if-else
一.分段函数 二.if - else if if(expl) st1; else if(exp2) st2; else st3; 单一出口,尽量不要把代码写死
22.多路分支switch-case语句
一.switch - case switch(type){ case 1: printf("你好"); break; case 2: printf("你好"); break; default: break; } switch(控制表达式){ case 常量: 语句; .... case 常量: 语句; .... default: 语句; .... } 注意:控制表达式只能是整数型的结果 常量可以是常数,也可以是常数计算的表达式 二.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位)的正整数,然后输出这个整数的位数。如: 输入:352,输出:3 人VS计算机 人的方式:眼睛一看就知道了 352->3位数 计算机的方式:判断数的范围来决定它的位数 352属于[100,999]->3位 人不擅长,因为人对数字的计算能力比文字弱 二.程序实现 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是几位? 数数! 人怎么数?从左往右数,一次划掉一个数字 计算机怎么划掉那个数字? 四.三位数逆序的题 352%100->52 那么234134123412341234%1000000000000->3241242143 怎么得到那个100000000000? 如果换一下,从右边开始划 13412341234123412/10->214124213412341 去掉最右边的数。然后? 不断地划,直到没数可以划... 在这个过程中计数 五.试试代码 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循环 在进入循环的时候不做检查,而是在执行完一轮循环体的代码之后,再来检查循环的条件是否满足,如果满足则继续下一轮循环,不满足则结束循环 do{ <循环体语句> }while(<循环条件>); 三.两种循环 do-whlie循环和while循环很像,区别是在循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时结束循环
26.猜数游戏
一. 1.让计算机来想一个数,然后让用户来猜,用户每输入一个数,就告诉它是大了还是小了,直到用户猜中为止,最后还要告诉用户它采了多少次 2.因为需要不断重复让用户猜,所以需要用到循环 3.在实际写出程序之前,我们可以先用文字描述程序的思路 4.核心重点是循环的条件 5.人们往往会考虑循环终止的条件 二. 1.计算机随机想一个数,记在变量number里 2.一个负责计次数count初始化为0 3.让用户输入一个数字a; 4.count递增(加一) 5.判断a和count的大小关系,如果a大,就输出“大”,如果a小就输出“小” 6.如果a和number是不相等的(无论大还是小),程序转回到第3步 7.否则,程序输出“猜中”和次数,然后结束 三.随机数 每次召唤rand()就得到一个随机的整数 示例: #include#include #include int main(){ srand(time(0)); int a=rand(); printf("%d\n",a); return 0; } 四.%100 x%n的结果是[0,n-1]的一个整数 五.完整代码示例: 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表示输入结束,然后程序计算出这些数字的平均数,输出输入的数字的个数和平均数 变量->算法->流程图->程序 二.变量 一个记录读到的整数的变量 平均数要怎么算? 只需要每读到一个数,就把它加到一个累加的变量里,到全部数据读完,再拿它去除读到的数的个数就可以了 一个变量记录累加的结果,一个变量记录读到的数的个数 三.算法 1.初始化变量sum和count为0; 2.读入number; 3.如果number不是-1,则将number加入sum,并将count加1,回到2 4.如果number是-1,则计算和打印出sum/count(注意换成浮点来计算) 示例: #includeint 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.整数求逆
一.整数的分解 一个整数是由1至多位数字组成的,如何分解出整数的各个位上的数字,然后加以计算 对一个整数做%10的操作,就得到它的个位数 对一个整数做/10的操作,就去掉了它的个位数 然后再对2的结果做%10,就得到原来数的十位数了 依此类推 二.数的逆序 输入一个正整数,输出逆序的数 结尾的0的处理 示例: #includeint 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 写一个程序,让用户输入n,然后计算输出n! 变量: 显然读用户的输入需要一个int的n,然后计算的结果需要用一个变量保存,可以是int的factor,在计算中需要有一个变量不断地从1递增到n,那可以是int的i 示例: 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 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 1x1还是1,所以程序的循环不需要从1开始,那么改成从多少开始合适呢?这样修改之后,程序对所有的n都正确吗?这样的改动有价值吗? 除了可以从1乘到n来计算n!,还可以从n乘到1来计算吧?试试把循环换个方向来计算n。这时候,还需要循环控制变量i吗? 示例: 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;i31.循环控制:如何用break和continue来控制循环
一.素数 只能被1和自己整除的数,不包括1 2,3,5,7,11,13,17,19 示例: #includeint main(){ int x; scanf("%d",&x); int i; int isPrime=1; //x是素数 for (i=2;i 32.循环嵌套
一.100以内的素数 如何写程序输出100以内的素数? 二.嵌套的循环 循环里面还是循环 循环输出50个素数 三.凑硬币 如何用1角、2角和5角的硬币凑出10元以下的金额呢? break和continue只能对它所在的那层循环做 跳出所有循环可以使用接力break 示例: int x; int one,two,five; int exit=0; scanf("%d",&x); for (one=1;one33.前n项求和
一. 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); 二.加减交替 int sign=1; sum+=sign*1.0/i; sign=-sign;34.正序分解整数
一. 输入一个非负整数,正序输出它的每一位数字 输入:13425 输出:1 3 4 2 5 二.格式化 怎么解决最后一个数据后面有带空格呢? 使用if判断 三. 1.先逆序再正序 do{ int d=x%10; t=t*10+d; x/=10; }while(x>0); do{ int d=x%10; printf("%d",d); if(x>9){ printf(" "); } x/=10; }while(x>0); 但是只能适用于数字末尾没有零的数字 2.使用求余和整除的方法 #includeint main(){ int mask=10000; int x; scanf("%d",&x); do{ int d=x/mask; printf("%d",d); if(mask>9){ printf(" "); } x %=mask; mask/=10; printf("x=%d,mask=%d,d=%d\n",x,mask,d); }while(mask>0); return 0; } 解决末尾为0的数字:更改判断的条件 3.改进的代码 int x; scanf("%d",&x); int mask=1; int t=x; while(t>9){ t/=10; mask*=10; } printf("x=%d,mask=%d\n",x,mask); do{ int d=x/mask; printf("%d",d); if (mask>9){ printf(" "); } x%=mask; mask/=10; }while(mask>0); printf("\n"); 35.求最大公约数
一. 输入两个数a和b,输出他们的最大公约数 输入:12 18 输出: 6 二.枚举 1.设t为2; 2.如果u和v都能被t整除,则记下这个t; 3.t加1后重复第2步,指导t等于u或v; 4.那么,曾经记下的最大的可以同时整除出u和v的t就是gcd 三.辗转相除法 1.如果b等于0,计算结束,a就是最大公约数; 2.否则,计算a除以b的余数,让a等于b,而b等于那个余数; 3.回到第一步 a b t 12 18 12 18 12 6 12 6 0 6 0 代码示例: 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位数 输入格式: 输入在一行中给出A 输出格式: 输出满足条件的3位数,要求从小到大,每行6个整数,整数间以空格分隔,但行末不能有多余空格 答案代码: int a; scanf("%d",&a); int i,j,k; i=a; int cnt =0; while(i<=a+3){ j=a; while(j<=a+3){ k=a; while(k<=a+3){ if( i!=j){ if(i!=k){ if(j!=k){ cnt++; printf("%d%d%d",i,j,k); if (cnt==6){ printf("\n"); cnt=0; }else{ printf(" "); } } } } k++; } j++; } i++; } 二.水仙花数(20) 题目: 水仙花数是指一个N位正整数(N>=3),它的每个位上的数字的N次幂之和等于它本身。例如:153=1^3+5^3+3^3。本题要求编写程序,计算所有N位水仙花数 输入格式: 输入在一行中给出一个正整数N(3<=N<=7) 输出格式: 按递增顺序输出所有N位水仙花数,每个数字占一行 答案代码: int n; scanf("%d",&n); n=3; int first =1; int i=1; while(i0); if(sum==i){ printf("%d\n",i); } i++; } 三.打印九九口诀表 本题要求对任意给定的1为正整数N,输出1*1到N*N的部分口诀表 输入格式: 输入在一行中给出一个正整数N(1<=N<=9) 输出格式: 输出下三角N*N部分口诀表,其中等号右边数字占4位、左对齐 答案代码: int n; scanf("%d",&n); int i,j; i=1; while(i<=n){ j=1; while(j<=i){ printf("%d*%d=%d",i,j,i*j); if (i*j<10){ printf(" "); } else{ printf(" "); } j++; } printf("\n"); i++; } 四.统计素数并求和 本题要求统计给定整数M和N区间内素数的个数并对他们求和 输入格式: 输入一行中欧给你给出2个正整数M和N(1<=M<=N<=500) 输出格式: 在一行中顺序输出M和N区间内素数的个数以及他们的和,数字间以空格分隔 答案代码: int m,n; int i; int cnt=0; int sum=0; scanf("%d %d",&m,&n); if (m==1) m=2; for(i=m;i<=n;i++){ int isPrime=1; int k; for(k=2;k 3)提示"Good Guess!";如果超过N次都没有才到,则提示"Game Over",并结束程序。如果在到达N次之前,用户输入了一个负数,也输出"Game Over",并结束程序 输入格式: 输入第一行中给出2个不超过100的正整数,分别是系统产生的随机数、以及猜测的最大次数N。随后每行给出一个用户的输入,直到出现负数位置 输出格式: 在一行中输出每次猜测相应的结果,直到输出猜对的结果或"Game Over"则结束 答案代码: int number,n; int inp; int finished =0; int cnt=0; scanf("%d %d",&number,&n) do{ scanf("%d",&inp); cnt++; if(inp<0){ printf("Game Over\n"); finished=1; } else if(inp>number){ printf("Too big\n"); } else if(inp 0){ t=a%b; a=b; b=t; } printf("%d/%d\n",dividence/a,divisor/a); 八.念数字 输入一个整数,输出每个数字对应的拼音。当整数为负数时,先输出“fu”字,十个数字对应的拼音如下: 0:ling 1:yi 2:er 3:san 4:si 5:wu 6:liu 7:qi 8:ba 9:jiu 输入格式: 输入在一行中给出一个整数:如:1234 提示:整数包括负数、零和正数 输出格式: 在一行中输出这个整数对应的拼音,每个数字的拼音之间用空格分开,行末没有最后的空格是。如yi er san si。 答案代码: int x; scanf("%d",&x); if(x<0){ printf("fu "); x=-x; } int mask=1; int t=x; while(t>9){ t/=10; mask*=10; } do{ int d =x/mask; switch (d){ case 0:printf("ling");break; case 1:printf("yi");break; case 2:printf("er");break; case 3:printf("san");break; case 4:printf("si");break; case 5:printf("wu");break; case 6:printf("liu");break; case 7:printf("qi");break; case 8:printf("ba");break; case 9:printf("jiu");break; } if(mask>9)printf(" "); x%=mask; mask/=10; }while(mask>0); printf("\n"); 九.求a的连续和 输入两个整数a和n,a的范围是[0,9],n的范围是[1,8],求数列之和S=a+aa+aaa+...+aaa...a(n个a)。如a为2、n为8时的是2+22+222+...+22222222的和 输入格式: 输入在一行中给出两个整数,先后表示a和n 输出格式: 在一行中输出要求的数列之和 答案代码: int a,n; scanf("%d %d",&a,&n); int sum=0; int i; int t=0; for (i=0;i 47.数据类型
一.C是有类型的语言 C语言的变量,必须: 在使用之前定义,并且 确定类型 C以后的语言向两个方向发展: C++/Java更强调类型,对类型的检查更严格 JavaScript、python、PHP不看重类型,甚至不需要事先定义 二.类型安全 支持强类型的观点认为明确的类型有助于尽早发现程序中的简单错误 反对强类型的观点认为过于强调类型强迫程序员面对底层、实现而非事务逻辑 总得来说,早期语言强调类型,面向底层的语言强调类型 C语言需要类型,但是对类型的安全检查并不足够 三.C语言的类型 整数 char、short、int、long、long long 浮点数 float、double、long double 逻辑 bool 指针 自定义类型 四.类型有何不同 类型名称:int、long、double 输入输出时的格式化:%d,%ld,%lf 所表达的数的范围:char48.整数类型
一. short是2个字节 long是8个字节 long long也是8个字节 二. char:1字节(8比特(bit)) short:2字节 int:取决于编译器(CPU),通常的意义是“1个字” long:取决于编译器(CPU),通常的意义是“1个字” long long:8个字49.整数的内部表达
一. 计算机内部一切都是二进制 18->00010010 0->00000000 -18->? 二.如何表示负数 十进制用'-'来表示负数,在做计算的时候 三.二进制负数 1个字节可以表达的数: 00000000--11111111(0-255) 三种方案: 1.仿照十进制,有一个特殊的标志表示负数 2.取中间的数为0,如1000000表示0,比它小的是负数,比他大的是正数 3.补码 考虑-1,我们希望-1+1->0。如何能做到? 0->00000000 1->00000001 11111111 + 00000001->100000000(多于8bit的部分直接丢掉) 因为0-1 -> -1,所以, -1= (1)00000000 - 00000001 ->11111111 11111111被当作纯二进制看待时,是255,被当作补码看待时是-1 同理,对于-a,其补码就是0-a,实际是2^n-a,n是这种类型的位数 补码的意义就是拿补码和原码可以加出一个溢出的“零”50.整数的范围
一.数的范围 对于一个字节(8位),可以表达的是: 00000000-11111111 其中 00000000->0 11111111~10000000->-1~-128 00000001~01111111->1~127 二. char:1字节:-128~-127(负数比正数多一个(多了个0)) short:2字节:-32768~32767 int:取决于编辑器(CPU),通常的意义是“1个字 long:4字节 long long:8字节 三.unsigned 如果一个自变量常熟想要表达自己是unsigned,可以在后面加u或U 255U 用l或L表示long(long) unsigned的初衷并非扩展数能表达的范围,而是为了做纯二进制运算,主要是为了移位(扩大了正数的范围,但是不能表达负数了) 四.整数越界 整数是以纯二进制方式进行计算的,所以: 11111111+1->100000000->0 01111111+1->10000000->128 10000000-1->01111111->127 示例: char c=127; c=c+1; printf("%d",c); 结果为-128 能找到int数据类型表示的最大数的代码: int a=0; while(++a>0){ printf("int数据类型表示的最大数是:%d\n",a-1); }50.整数的格式化输出
一.整数的输入输出 只有两种形式:int或long long %d:int %u:unsigned %ld:long long %lu:unsigned long long 二.8进制和16进制 一个以0开始的数字字面量是8进制 一个以0x开始的数字字面量是16进制 %o用于8进制,%x用于16进制 8进制和16进制只是如何把数字表达为字符串,与内部如何表达数字无关 16进制很适合表达二进制数据,因为4位二进制正好是一个16进制位 8进制的一位数字正好表达3为二进制 因为早起计算机的字长是12的倍数,而非851.浮点类型
一. 类型 字长 范围 有效数字 float 32 7 double 64 15 二.浮点的输入输出 类型 scanf printf float %f %f,%e double %lf %f,%e(如果是写E,那么输出的结果也会换成E) 三.科学计数法 double ff=1e-10; printf("%f\n",ff) 四.输出精度 在%和f之间加上.n可以指定输出小数点后几位,这样的输出是4舍5入的52.浮点的范围与精度(视频重新看(代码示例需要记录))
一.超过范围的浮点数 printf输出inf表示超过范围的浮点数:无穷 printf输出nan表示不存在的浮点数 printf("%f",12.0/0.0);结果为inf 二.浮点运算的精度 带小数点的字面量是double而非float float需要用f或F的后缀来表明身份 三.浮点数的内部表达 浮点数在计算时是由专用的硬件部件实现的 计算double和float所用的部件是一样的 四.选择浮点类型 如果没有特殊需要,只使用double 现代CPU能直接对double做硬件运算,性能不会比float差,在64位的机器上,数据存储的速度也不比float慢53.字符类型
一.字符类型 char是一种整数,也是一种特殊的类型:字符。这是因为: 用单引号表示的字符字面量:'1','a' ''也是一个字符 printf和scanf里用%c来输入输出字符 二.字符的输入输出 如何输入'1'这个字符给char c? scanf("%c",&c);->1 scanf("%d",&i);c=i;->49 '1'的ASCII编码是49,所以当c==49时,它代表‘1’ 一个49各自表述 三.混合输入 有何不同? scanf("%d %c",&i,&c); 四.字符计算 一个字符加一个数字得到ASCII码表中那个数之后的字符 两个字符的减,得到他们在表中的距离 五.大小写转换 字母在ASCII表中是顺序排列的 大写字母和小写字母是分开排列的,并不在一起 'a'-'A'可以得到两段之间的距离,于是 a+'A'-'a'可以把一个大写字母变成小写字母,而a+'a'-'A'可以把一个小写字母变成大写字母54.逃逸字符
一.逃逸字符 用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠'\'开头,后面跟上另一个衣服,这两个字符合起来,组成了一个字符 示例: printf("请分别输入身高的英尺和英寸," "如输入\"5 7\"表示5英尺7英寸"); 字符 意义 字符 意义 \b 回退一格 \" 双引号 \t 到下一个制表位 \' 单引号 \n 换行 \\ 反斜杠本身 \r 回车 回退一格,示例: printf("123\bA\n1234");结果3被删掉了 二.制表位 每行的固定位置 一个\t使得输出从下一个制表位开始 用\t才能使得上下两行对齐 固定位置,示例: printf("123\t456\n"); printf("12\t456\n"); 三.回车换行 源自打字机的动作55.类型转换
一.自动类型转换 当运算符的两边出现不一致的类型时,会自动转换成较大的类型 大的意思是能表达的数的范围更大 char->short->int->long->long long int->float->double 对于printf,任何小于int的类型会被转换成int;float会被转换成double 但是scanf不会,要输入short,需要%hd 二.强制类型转换 要把一个量强制转换成另一个类型(通常是较小的类型),需要: (类型)值 比如: (int)10.2 (short)32 注意这时候的安全性,小的变量不总能表达大的量 (short)32768 只是从那个变量计算出了一个新的类型的值,它并不改变那个变量,无论是值还是类型都不改变 强制类型转换的优先级高于四则运算56.逻辑类型
一.bool #include之后就可以使用bool和true、false 57.逻辑运算
一. 逻辑运算是对逻辑量进行的运算,结果只有0或1 逻辑量是关系运算或逻辑运算的结果 运算符 描述 示例 结果 ! 逻辑非 !a 如果a是true结果就是false,... && 逻辑与 a&&b 如果a和b都是true,结果就是true,... || 逻辑或 a||b 如果a和b有一个是true,结果就是true,... 二. 如果要表达数学中的区间应该如何写C的表达式? 像44 && x<6 如何判断一个字符c是否是大写字母? c>='A' && c<='Z' 三.优先级 ! > && > || 四.短路 逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算 a==6 && b==1 a==6 && b+=1 对于&&,左边是false时就不做右边了 对于||,左边是true时就不做右边了 不要把赋值,包括复合赋值组合进表达式! 58.条件运算和逗号运算
一.条件运算符 count=(count>20)?count-10:count+10 相当于 if (count>20) count=count-10 else count=count+10 条件、条件满足时的值和条件不满足时的值 二.优先级 条件运算符的优先级高于赋值运算符,但是低于其他运算符 三.嵌套表达式 条件运算符是自右向左结合的 我们不希望你使用嵌套的条件表达式 四.逗号运算 (1)逗号用来连接两个表达式,并以其右边的表达式的值作为它的结果。逗号的优先级是所有的运算符中最低的,所以它两边的表达式会先计算;逗号的组合关系是自左向右,所以左边的表达式会先计算,而右边的表达式的就留下来作为逗号运算的结果。示例: i=3+4,5+6 i=(3+4,5+6) (2)在for中使用 for(i=0,j=10;i59.初见函数
一.素数求和 int isPrime(int i){ int ret=1; int k; for (k=2;k60.函数的定义和使用
一.什么是函数? 函数是一块代码,接收零个或多个参数,做一件事情,并返回零个或一个值 可以先想像成数学中的函数 函数头 { 函数体 } 函数头: **返回类型** 函数名(参数表) 二.调用函数 函数名(参数值): ()起到了表示函数调用的重要作用 即使没有参数也需要() 如果有参数,则需要给出正确的数量和顺序 这些值会被按照顺序依次用来初始化函数中的参数 调试中的单步进入是用于进入函数内部观察的 三.函数返回 函数知道每一次是哪里调用它,会返回到正确的地方61.从函数中返回
一.从函数中返回值 return停止函数的执行,并送回一个值 return; return 表达式; 一个函数里可以出现多个return语句 可以赋值给变量 可以再传递给函数 甚至可以丢弃 二.没有返回值的函数 void 函数名(参数表) 不能使用带值的return 可以没有return 调用的时候不能做返回值的赋值 如果函数有返回值,则必须使用带值的return62.函数原型
一.函数先后关系 像这样把sun()写在上面,是因为: C的编译器自上而下顺序分析你的代码 在看到sum(1,10)的时候,它需要知道sum()的样子 也就是sum()要几个参数,每个参数的类型如何,返回什么类型 这样它才能检查你对sum()的调用是否正确 二.如果不知道 也就是把要调用的函数放到下面了 三.函数原型 函数头,以分号“;”结尾,就构成了函数的原型 函数原型的目的是告诉编译器这个函数长什么样 名称 参数(数量及类型) 返回类型 旧标准习惯把函数原型写在调用它的函数里面(main里面) 现在一般写在调用它的函数前面63.参数传递
一.调用函数 如果函数有参数,调用函数时必须传递给它数量、类型正确的值 可以传递给函数的值时表达式的结果,这包括: 字面量 变量 函数的返回值 计算结果 二.类型不匹配? 调用函数时给的值域参数的类型不匹配是C语言传统上最大的漏洞 编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的 后续的语言,C++/Java在这方面很严格 三.传过去的是什么? 这样的代码能交换a和b的值 C语言在调用函数时,永远只能传值给函数 四.传值 每个函数有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系 过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给的值,叫做“实际参数” 由于容易让初学者误会实际参数就是实际在函数中进行计算的参数,误会调用函数的时候把变量而不是值传进去了,所以我们不建议继续用这种古老的方式去称呼他们 我们认为,他们是参数和值的关系64.本地变量
一.本地变量 函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量 定义在函数内部的变量就是本地变量 参数也是本地变量 二.变量的生存期和作用域 生存期:什么时候这个变量开始出现了,到什么时候它消亡了 作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用) 对于本地变量,这两个问题的答案是统一的:大括号内--块 三.本地变量的规则 本地变量是定义在块内的 它可以是定义在函数的块内 也可以定义在语句的块内,示例: if(a65.函数庶事
一.没有参数时 void f(void); 还是 void f(); 在传统C中,它表示f函数的参数表位置,并不表示没有参数 二.逗号运算符? 调用函数时的逗号和逗号运算符怎么区分? 调用函数时的圆括号里的逗号是标点符号,不是运算符 f(a,b) f((a,b)) 三.函数里的函数? C语言不允许函数嵌套定义 四.这是什么? int i,j,sum(int a,int b); return (i); 五.关于main int main()也是一个函数 要不要协程int main(void)? return的0有人看吗? Windows:if error level 1... Unix Bash:echo $? Csh:echo $status66.初试数组
一. (1)如何写一个程序计算用户输入的数字的平均数? 不需要记录输入的每一个数->循环scanf (2)如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数? 二.如何记录很多数? int num1,num2,num3... 三.数组 int number[100]; scanf("%d",&number[i]); 示例: while(x!=1){ number[cnt]=x; sum+=x; cnt ++; scanf("%d",&x); } 1.定义数组 2.对数组中的元素赋值 3.使用数组中的元素 4.遍历数组 这个程序存在安全隐患,是什么? 因为输入的数据可能超过10067.定义数组
一.定义数组 <类型> 变量名称[元素数量]; int grades[100]; double weight[20]; 元素数量必须是整数 C99之前:元素数量必须是编译时刻确定的字面量 二.数组 是一种容器(放东西的东西),特点是: 其中所有的元素具有相同的数据类型; 一旦创建,不能改变大小 数组中的元素在内存中是连续依次排列的 三.int a[10] 一个int的数组 10个单元:a[0],a[1],...,a[9] 每个单元就是一个int类型的变量 可以出现在赋值的左边或右边 a[2]=a[1]+6; 在赋值左边的叫做左值 四.数组的单元 数组的每个单元就是数组类型的一个变量 使用数组时放在[]中的数字叫做下标或索引,下标从0开始计数: grades[0] grades[99] averages[5] 五.有效的下标范围 编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写 一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃 segmentation fault 但是也可能运气好,每造成严重的后果 所以这时程序员的责任来保证程序只使用有效的下标值:[0,数组的大小-1] 如果先让用户输入有多少数字要计算,可以用C99的新功能来实现 六.长度为0的数组? int a[0]; 可以存在,但没有用68.数组的例子:统计个数
一. 写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束 代码: const int number=10; int x; int count[number]; int i; for (i=0;i<10;i++){ count[i]=0; } scanf("%d",&x); while(x!=-1){ if(x>=0 && x<=9){ count[x]++; } scanf("%d",&x); } for(i=0;i<10;i++){ printf("%d:%d\n",i,count[i]); } 1.数组的大小 2.定义数组 3.初始化数组 4.数组参与运算 5.遍历数组输出69.数组运算
一. 在一组给定的数据中,如何找出某个数据是否存在? 二.数组的集成初始化 int a[]={2,4,5234,12,4}; 直接用大括号给出数组的所有元素的初始值 不需要给出数组的大小,编译器替你数 int a[13]={2}; 结果是第一位数据是2 其他位数据都是0 三.集成初始化时的定位 int a[10]={ [0]=2,[2]=3,6, }; 结果为第一位数据是2,第三位数据是3,第四位数据是6,其他数据都是0 用[n]在初始化数据中给出定位 没有定位的数据接在前面的位置后面 其他位置的值补零 也可以不给出数据大小,让编译器算 特别适合初始数据稀疏的数组 四.数组的大小 sizeof给出整个数组所占据的内容的大小,单位是字节 sizeof(a)/sizeof(a[0]) sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数 这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码 最后留不留逗号都没事 五.数组的赋值 数组变量本身不能被赋值 要把一个数组的所有元素交给另一个数组,必须采用遍历 for(i=0;i70数组例子-素数(重新看)
一. 从2到x-1测试是否可以整除 对于n要循环n-2遍,当n很大时就是n遍 去掉偶数后,从3到x-1,每次加2 如果x是偶数,立刻 否则要循环(n-3)/2+1遍 当n很大时就是n/2遍 无序x-1,到sqrt(x)就够了 只需要sqrt(x)遍 示例: int isPrime(int x){ int ret =1; int i; if(x==1||(x%2==0 && x!=2) ret =0; for (i=3;i71.二维数组
一.二维数组 int a[3][5]; 通常理解为a是一个3行5列的矩阵 二.二维数组的遍历 for(i=0;i<3;i++){ for(j=0;j<5;j++){ a[i][j]=i*j; } } a[i][j]是一个int 表示第i行第j列上的单元 a[i,j]是什么?相当于a[j] 三.二维数组的初始化 int a[][5]={ {0,1,2,3,4}, {2,3,4,5,6}, }; 列数是必须给出的,行数可以由编译器来数 每一行一个{},逗号分隔 最后的逗号可以存在,有古老的传统 如果省略,表示补零 也可以用定位 四.tic-tac-toe游戏 读入一个3*3的矩阵,矩阵中的数字为1表示该位置上有一个X,为0表示O 程序判断这个矩阵中是否有获胜的一方,获胜一方的字符X或O,或输出无人获胜 代码: const int size=3; int board[size][size]; int i,j; int num0fx; int num0f0; int result=-1 // -1:没人赢,1:X赢,0:O赢 //读入矩阵 for(i=0;i72.取地址运算
一.运算符& scanf("%d",&i);里的& 获得变量的地址,它的操作数必须是变量 int i;printf("%x",&i); 地址的大小是否与int相同取决于编译器 int i; printf("%p",&i); 二.&不能取的地址 &不能对没有地址的东西取地址 &(a+b)? &(a++)? &(++a)? 三.试试这些& 变量的地址 相邻的变量的地址 &的结果的sizeof 数组的地址 数组单元的地址 相邻的数组单元的地址73.指针
一.scanf 如果能够将取得的变量的地址传递给一个函数,能否通过这个地址在那个函数内访问这个变量? scanf("%d",&i); scanf()的原型应该是怎样的?我们需要一个参数能保存别的变量的地址,如何表达能够保存地址的变量? 二.指针 就是保存地址的变量 int i; int* p =&i; int* p,q; int *p,q;(q不是指针类型) 三.作为参数的指针 void f(int *p); 在被调用的时候得到了某个变量的地址; int i=0;f(&i); 在函数里面可以通过这个指针访问外面的这个i 四.访问那个地址上的变量* *是一个单目运算符,用来访问指针的值所表示的地址上的变量 可以做右值也可以做左值 int k=*p; *p=k+1; 示例: void f(int *p){ printf("p=%p\n",p);//结果为地址 printf("*p=%d\n",*p);//结果为6 *p=26; } void g(int i){ printf("k=%d\n",k);//结果为26 } int main(void){ int i=6; printf("&i=%p\n",&i);//结果为地址 f(&i); } 五.传入地址 为什么 int i;scanf("%d",i); 编译没有报错?74.指针的使用
一.指针应用场景一 交换两个变量的值 void swap(int *pa,int *pb){ int t=*pa; *pa=*pb; *pb=t; } 二.指针应用场景二a 函数返回多个值,某些值就只能通过指针返回 传入的参数实际上是需要**保存带回的结果的变量** 三.指针应用场景二b 函数返回运算的状态,结果通过指针返回 常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错: -1或0(在文件操作会看到大量的例子) 但是当任何数值都是有效的可能结果时,就得分开返回了 后序的语言(C++,Java)采用了异常机制来解决这个问题 四.指针最常见的错误 **定义了指针变量,还没指向任何变量,就开始使用指针**75.指针与数组
一.传入函数的数组成了什么? 函数参数表中的数组实际上是指针 sizeof(a)==sizeof(int*) 但是可以用数组的运算符来进行运算 二.数组参数 以下四种函数原型是等价的: int sum(int*ar,int n); int sum(int*,int); int sum(int ar[],int n); int sum(int [],int); 三.数组变量是特殊的指针 数组变量本身表达地址,所以 int a[10];int*p=a;//无需用&取地址 但是数组的单元表达的是变量,需要用&取地址 a==&a[0] []运算符可以对数组做,也可以对指针做; p[0]<==>a[0](int *p=&min;p[0](把min看成是min[1]数组变量)) *运算符可以对指针做,也可以对数组做; *a=25; 数组变量是const的指针,所以不能被赋值 int a[]<==>int * const a=...76.指针与const
一. 指针--可以是const 值--可以是const 二.指针是const 表示一旦得到了某个变量地址,不能再指向其他变量 int*const q=&i;//q是const *q=26;//OK q++;//ERROR 三.所指的是const 表示不能通过这个指针去修改那个变量(并不能使得那个变量称为const) const int*p=&i; *p =26;//ERROR!(*p)是const i=26;//OK p=&j;//OK 四.这些事啥意思? int i; const int*p1=&i; int const*p2=&i; int *const p3=&i; 判断哪个被const了的标志是const在*前面还是后面 五.转换 总是可以把一个非const的值转换成const的 void f(const int*x); int a=15; f(&a);//OK const int b=a; f(&b);//OK b=a+1;//Error! 当要传递的参数的类型比地址大的时候,这是常用的手段;既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改 六.const数组 const int a[]={1,2,3,4,5}; 数组变量已经传入函数时传递的是地址,所以那个函数内部可以修改数组的值 为了保护数组不被函数破坏,可以设置参数为const int sum(const int a[],int length);77.指针运算
一.1+1=2? 给一个指针+1表示要让指针指向下一个变量 int a[10]; int *p=a; *(p+1)->a[1] 如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义 二.指针计算 这些算术运算可以对指针做: 给指针加、减一个整数(+,+=,-,-=) 递增递减(++/--) 两个指针相减(表示之间有几个东西(/sizeof)) 三.*p++ 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去 *的优先级虽然高,但是没有++高 常用于数组类的连续空间操作 在某些CPU上,这可以直接被翻译成一条汇编指令 四.指针比较 <,<=,==,>,>=,!=都可以对指针做 比较他们在内存中的地址 数组中的单元的地址肯定是线性递增的 五.0地址 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址 所以你的指针不应该具有0值 因此可以用0地址来表示特殊的事情 返回的指针是无效的 指针没有被真正初始化(先初始化为0) NULL是一个预定义的符号,表示0地址 有的编译器不愿意你用0来表示0地址 六.指针的类型 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址 但是指向不同类型的指针是不能直接相互赋值的 这是为了避免用错指针 七.指针的类型转换 void*表示不知道指向什么东西的指针 计算时与char*相同(但不相通) 指针也可以转换类型 int*p=&i;void*q=(void*)p; 这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量 我不再当你是int啦,我认为你就是个void! 八.用指针来做什么 1.需要传入较大的数据时用做参数 2.传入数据后对数组做操作 3.函数返回不止一个结果 4.需要用函数来修改不止一个变量 5.动态申请的内存...78.动态内存分配
一.输入数据 如果输入数据时,先告诉你个数,然后再输入,要记录每个数据 C99可以用变量做数组定义的大小,C99之前呢? int *a=(int*)malloc(n*sizeof(int)); malloc在stdlib.h库中 free() 二.malloc #includevoid*malloc(size_t size) 向malloc申请的空间的大小是以字节为单位的 返回的结果是void*,需要类型转换为自己需要的类型 (int*)malloc(n*sizeof(int)) 三.没空间了? 如果申请失败则返回0,或者叫做NULL 你的系统能给你多大的空间? 测试运行空间的代码 void *p; int cnt=0; while((p=malloc(100*1024*1024))){ cnt++; } printf("分配了%d00MB的空间\n",cnt); 四.free() 把申请得来的空间还给“系统” 申请过的空间,最终都应该要还 混出来的,迟早都是要还的 只能还申请来的空间的首地址 五.常见问题 申请了没free->长时间运行内存逐渐下降 新手:忘了 老手:找不到合适的free的时机 free过了再free 地址变过了,直接去free 79.字符串
一.字符数组 char word[]={'H','e','I','I','o','!'}; 这不是C语言的字符串,因为不能用字符串的方式做计算 char word[]={'H','e','I','I','o','!','\0'}; 二.字符串 以0(整数0)结尾的遗传字符 0或'\0'是一样的,但是和'0'不同 0标志字符串的结束,但它不是字符串的一部分 计算字符串长度的时候不包含这个0 字符串以数组的形式存在,以数组或指针的形式访问 更多的是以指针的形式 string.h里有很多处理字符串的函数 三.字符串变量 char*str="Hello"; char word[]="Hello"; char line[10]="Hello"; 四.字符串常量 "Hello" "Hello"会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0 两个相邻的字符串常量会被自动连接起来 五.字符串 C语言的字符串是以是字符串数组的形态存在的 不能用运算符对字符串做运算 通过数组的方式可以遍历字符串 唯一特殊的地方是字符串字面量嗯可以医用来初始化字符串数组 以及标准库提供了一系列字符串函数80.字符串变量
一.字符串常量 char*s="Hello,world!"; s是一个指针,初始化为指向一个字符串常量 由于这个常量所在的地方,所以实际上s是const char* s,但是由于历史的原因,编译器接收不带const的写法 但是试图对s所指的字符串做写入会导致严重的后果 如果需要修改字符串,应该用数组; char s[]="Hello,world!"; 二.指针还是数组? char *str="Hello"; char word[]="Hello"; 数组:这个字符串在这里 作为本地变量空间自动被回收 指针:这个字符串不知道在哪里 处理参数 动态分配空间 如果要处理一个字符串->数组 如果要处理一个字符串->指针 三.char*是字符串? 字符串可以表达为char*的形式 char*不一定是字符串 本意是指向字符的指针,可能指向的是字符的数组(就像int*一样) 只有它所指的字符串数组有结尾的0,才能说它所指的是字符串81.字符串的输入和输出
一.字符串赋值? char*t="title"; char*s; s=t; 并没有产生新的字符串,只是让指针s指向了t所指的字符串,对s的任何操作就是对t做的 二.字符串输入输出 char string[8]; scanf("%s",string); printf("%s",string); scanf读入一个单词(到空格、tab或回车为止) scanf是不安全的,因为不知道要读入的内容的长度 三.安全的输入 char string[8]; scanf("%7s",string); 在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小一 下一次scanf从哪里开始? 四.常见错误 char *string; scanf("%s",string); 以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用了 由于没有对string初始化为0,所以不一定每次运行都出错 五.空字符串 char buffer[100]=""; 这是一个空的字符串,buffer[0]='\0'; char buffer[]=""; 这个数组的长度只有1!82.字符串数组
一.字符串数组 char **a; a是一个指针,指向另一个指针,那个指针指向一个字符(串) char a[][] 二.程序参数 int main(int argc,char const*argv[]) argv[0]是命令本身 当使用Unix的符号链接时,反映符号链接的名字,示例: int i; for(i=0;i83.单字符输入输出
一.putchar int putchar(int c); 向标准输出写一个字符 返回写了几个字符,EOF(-1)表示写失败 二.getchar int getchar(void); 从标准输入读入一个字符 返回类型是int是为了返回EOF(-1)84.字符串函数
一.string.h strlen strcmp strcpy strcat strchr strstr 二.strlen size_t strlen(const char*s); 返回s的字符串长度(不包括结尾的0) 三.strcmp int strcmp(const char*s1,const char*s2); 比较两个字符串,返回: 0:s1==s2 1:s1>s2 -1:s185.枚举
一.常量符号化 用符号而不是具体的数字来表示程序中的数字 二.枚举 用枚举而不是定义独立的const int变量 三.枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法来声明: enum 枚举类型名字{名字0,...,名字n} enum colors{red,yellow,green}; 就创建了三个常量,red的值是0,yelllow是1,而green是2。 当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量值名字 枚举量可以作为值 枚举类型可以跟上enum作为类型 但是实际上是以整数来做内部计算和外部输入输出的 不区分大小写 示例: enum color{red,yellow,green}; void f(enum color c){ enum color t=red; scanf("%d",&t); f(t); return 0; } 四.套路:自动计数的枚举 这样需要遍历所有的枚举量或者需要建立一个用枚举量做下标的数组的时候就很方便了 五.枚举量 声明枚举量的时候可以指定值 enum COLOR{RED=1,YELLOW,GREEN=5}; 六.枚举只是Int 即使给枚举类型的变量赋值不存在整数值也没有任何waring或error 七.枚举 虽然枚举类型可以当作类型使用,**但是实际上很少用** 如果有意义上排比的名字,用枚举比const int方便 枚举比宏好,因为枚举有int类型89.复杂数据类型
一.用户自己建立数据类型 1. 数据的基本类型:整型、实型、字符型 数组时构造类型:每个元素为同一类型 有些问题仅用基本类型和数组来描述,无法反应其内在联系 2.结构体 结构体是一种构造数据类型 与其他高级语言中的“记录”类似 定义:由相互关联的不同数据类型的数据组成的有机整体 用途:为处理复杂的数据结构提供了手段 为函数间传递不同的参数提供了便利 关键字:struct 结构体类型定义 struct[结构体名]{ 类型标识符 成员名1; 类型标识符 成员名2; ... }; 注意:这只是声明一种数据类型并没有定义变量 二.定义结构体类型变量 1.先声明结构体类型,再定义结构体变量 struct 结构体名 变量名表列 示例: struct student stu1,stu2; 2.声明结构体类型的同时定义结构体变量 在大括号后面直接定义结构体类型变量 注意:只有在定义了结构体变量后系统才为其分配内存 3.说明 结构体类型与结构体变量概念不同 类型:不分配内存; 变量:分配内存 类型:不能赋值、存取、运算; 变量:可以 结构体成员名与程序中变量名可相同 结构体变量中欧给你的成员可单独使用,方法如普通变量 结构体可嵌套,成员也可以是以结构体变量 三.结构体变量的引用和初始化 1.引用结构体变量规则 (1)不能企图通过输出结构体变量名来达到输出结构体变量所有成员的值。只能对结构体变量中的各个成员分别进行输入和输出 (2)结构体变量不能整体引用,只能引用变量成员 结构体变量名.成员名 (3).是成员运算符 优先级:1 结合性:从左向右 (4)结构体成员本身又是一个结构体类型,则需要找到最低一级的成员 (5)结构体变量的成员与普通变量用法相同 (6)同类的结构体变量可以互相赋值 (7)可以引用结构体变量的地址,也可以引用结构体变量的地址(结构体变量的地址主要用作函数参数,传递结构体变量的地址)。 2.结构体变量的初始化 形式二 struct 结构体名 { 类型标识符 成员名1; 类型标识符 成员名2; .... }结构体变量={初始数据}; 四.结构体数组 1. 结构体数组:具有相同的结构体也可以组成数组 定义结构体数组:3种形式 形式一:间接定义 struct student stu[2]; 形式二:直接定义 struct student{ int num; }stu[2]; 形式三:直接定义 struct{ int num; }stu[2]; 五.指向结构体类型数据的指针 1. 存放结构体首地址 结构指针的运算按照C语言的地址运算原则进行 例如,结构指针+1将指向内存中下一个结构体 指向结构体变量的指针 定义形式: struct 结构体名 *结构体指针名 (存放结构体变量在内存的起始地址(就是结构体变量名)) 示例: struct student *p&stu; 2.通过指向结构体变量的指针变量输出结构体变量中成员的信息 如果p指向一个结构体变量stu,以下3种用法等价: (1)stu.成员名 stu.num (2)(*p)成员名 (*p).num (3)p->成员名 p->num90.文件(代码重新练一遍)
一.文件的基本概念和文件指针 1. 程序文件:源文件(.C)、目标文件(.obj)、可执行文件(.exe)等 数据文件:数据 2. 根据数据的组织形式:二进制文件和ASCII文件 二进制文件:数据在内存中是以二进制形式存储的,直接输出到外存 ASCII文件(文本文件):以ASCII代码形式存储,每一个字节存放一个字符的ASCII代码 3. 字符数据:以ASCII形式 数值型数据:可以用ASCII形式存储,也可以用二进制形式存储 4.文本文件:占存储空间多,需要转换时间,但方便字符处理 系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区 5.文件指针 每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息 文件信息保存在一个结构体变量中,类型为FILE 代码示例: typedef struct{ short level;//缓冲区“满”或“空”的程度 unsigned flags;//文件状态标志 char fd;//文件描述符 unsigned char hold;//如缓冲区五内容或不读取字符 short bsize;//缓冲区的大小 unsigned char *buffer;//数据缓冲区的位置 unsigned char* curp;//文件位置标记指针当前的指向 unsigned istemp;//临时问题件指示器 short token;//用于有效性检查 } 6.文件指针定义: FILE *fp;//定义一个指向FILE类型数据的指针变量 二.文件的打开和关闭 1. 使用前打开,结束后关闭 “打开”:为文件建立相应的信息区和文件缓冲区。 FILE*fp;//定义一个指向FILE类型数据的指针变量 “关闭”:是指撤销文件信息区和文件缓冲区,使文件指针变量不再指向文件 2.具体操作 FILE *fp;//定义一个指向FILE类型数据的指针变量 fp=fopen("a1",“r”);//将fopen函数的返回值赋给指针变量fp 以“读入”方式打开名字为a1的文件 3.信息 (1)需要打开文件的名字 (2)使用文件的方式(“读”还是“写”等) (3)哪一个指针变量指向被打开的文件 4.文件使用方式5.返回值: 成功返回文件指针 失败返回NULL 6.文件关闭:撤销文件信息区和文件缓冲区 fclose(文件指针); 7.fclose函数返回值: 关闭成功,返回0/EOF 关闭失败,返回-1 三.文本文件读写 1.fgetc(),fputc() fgetc:从参数文件指针所指向的文件中读取一个字符数据,读取正常返回读到的字符; 读到文件尾或出错,返回EOF 2.fputc(ch,fp):将参数ch表示到字符数据输出到文件指针fp指向的文件中,写入成功,返回值是字符数据ch,失败返回EOF 例题: 从键盘输入一些字符,并逐个把他们送到磁盘上去,直到用户输入一个“#”为止 思路: 文件指针指向要写入的文件 字符型变量保存输入的字符 循环输入直到输入#为止 代码示例:例题: 将file1问题件中的信息读出来,显示在屏幕上 代码示例:3.fgets函数的参数: (1)str字符数组 (2)n:整数,读入的字符个数 (3)fp:文件指针 注意: 第二个参数n是要求得到的字符个数 **实际只从文件中读入n-1个字符** 最后加一个字符串结束标志'\0' 4.读入结束: 但要将所遇到的换行符"\n"也作为一个字符读入 5.返回值:成功,返回str数组首地址 失败,返回NULL 调用: fgets(str,n,fp); 6.fputs函数的原型int fputs(char *str,FILE *fp); 参数:1.str:要写入文件的字符串指针 2.fp:文件指针 注意: 写入文件时,字符串末尾'\0'不写入 返回值:成功,0 失败,EOF gets和puts以终端为读写对象 fgets和fputs以指定的文件为读写对象 7.scanf、printf函数是相对于显示器终端的格式化的输入输出 fprinf、fscanf函数是相对于文件的格式化读写 fprinf(fp,"%d,%6.2f",i,f); fprintf(文件指针,格式字符串,输出表列); fprintf函数:将变量按指定的格式**写入**指定的文件 fscanf(文件指针,格式字符串,输出列表); fscanf函数:是从文件中按指定格式**读出**,存入相应的变量中 四.二进制文件读写 1.二进制读写: 写数据,直接将内存中数据原封不动、不加转换地赋值到磁盘文件 读数据,将磁盘文件中若干字节的内容一批读入内存 2.fread(buffer,size,count,fp); (1)buffer:从文件读入的数据存储区地址 (2)size:要读写的字节数 (3)count:读写数据项个数 (4)fp:文件类型指针 3.fwrite(buffer,size,count,fp); 代码示例: