当前位置: 首页 > news >正文

红河北京网站建设网站建设收费标准精英

红河北京网站建设,网站建设收费标准精英,discuz做门户网站,建网站好还是开天猫好分支和循环语句 1. 前言2. 预备知识2.1 getchar函数2.2 putchar函数2.3 计算数组的元素个数2.4 清屏2.5 程序的暂停2.6 字符串的比较 3. 结构化3.1 顺序结构3.2 分支结构3.3 循环结构 4. 真假性5. 分支语句#xff08;选择结构#xff09;5.1 if语句5.1.1 语法形式5.1.2 else… 分支和循环语句 1. 前言2. 预备知识2.1 getchar函数2.2 putchar函数2.3 计算数组的元素个数2.4 清屏2.5 程序的暂停2.6 字符串的比较 3. 结构化3.1 顺序结构3.2 分支结构3.3 循环结构 4. 真假性5. 分支语句选择结构5.1 if语句5.1.1 语法形式5.1.2 else和if的匹配5.1.3 代码风格 5.2 switch语句5.2.1 语法形式5.2.2 break语句5.2.3 default语句 6. 循环语句6.1 while循环6.1.1 语法形式6.1.2 break语句6.1.3 continue语句6.1.4 代码举例 6.2 for循环6.2.1 语法形式6.2.2 break语句6.2.3 continue语句6.2.4 for循环控制循环变量6.2.5 for循环的变种 6.3 do while循环6.3.1 语法形式6.3.2 break语句6.3.3 continue语句 7. goto语句7.1 语法形式7.2 代码举例 8. 综合练习8.1 打印1~100之间的奇数8.2 求n的阶乘8.3 二分查找折半查找8.4 字符串从两端向中间打印8.5 模拟登陆情景8.6 猜数字游戏8.7 把三个整数从大到小输出8.8 求最大公约数8.9 求最小公倍数8.10 打印闰年8.11 打印素数8.12 统计9的个数8.13 分数求和8.14 求10个整数的最大值8.15 打印乘法口诀表 1. 前言 大家好我是努力学习游泳的鱼。这篇文章将会讲解C语言里的重头戏分支和循环语句。由于这部分内容细节较多本文可能会比较长希望大家耐心阅读。如果短时间内看不完可以先点下收藏防止迷路。如果你能够掌握本篇文章的内容你对C语言的理解就更上一层楼啦感谢大家的支持 2. 预备知识 2.1 getchar函数 getchar用于从键盘中读取字符并返回读取到的字符的ASCII码值。如果遇到读取错误或者文件结束则返回EOF。 getchar不需要传参数返回值类型是int对应的头文件是stdio.h。 #include stdio.hint main() {int ch getchar(); // 用int类型变量来接收返回值printf(%c\n, ch);return 0; }读取失败会返回EOF在windows操作系统中只需要按CtrlZ并敲回车即可让getchar返回EOF. 2.2 putchar函数 putchar用于打印一个字符到屏幕上使用时只需要传你要打印的字符的ASCII值。可以配合getchar使用 #include stdio.hint main() {int ch getchar();putchar(ch);// 或者直接写(这么写有风险因为有可能读取失败)putchar(getchar());return 0; }2.3 计算数组的元素个数 假设有一个数组int arr[] {1,2,3,4,5};我们如何用代码计算该数组的元素个数呢很简单用整个数组的大小除以第一个元素的大小就行了。int sz sizeof(arr) / sizeof(arr[0]); // sz为数组的元素个数整个数组的大小是5个int即20个字节第一个元素是int即4个字节20÷45就算出sz为5了。 2.4 清屏 system(cls);代码可以完成清屏其中system函数的使用需要引用头文件stdlib.h。 2.5 程序的暂停 比如Sleep(1000);可以让程序暂停1000毫秒其中Sleep函数的使用需要引用头文件Windows.h。 2.6 字符串的比较 不能使用来比较字符串而应使用strcmp函数对应的头文件是string.h。 strcmp函数可以用来比较两个字符串如果两个字符串相等就返回0。 3. 结构化 生活中有三种结构分别是顺序结构分支结构和循环结构。这三种结构C语言都能支持所以说 C语言是一门结构化的程序设计语言。 3.1 顺序结构 即一条路走到黑具体体现是代码会一条一条依次执行。 3.2 分支结构 C语言里有if语句和switch语句。 3.3 循环结构 C语言里有三种循环分别是while循环for循环和do while循环。 4. 真假性 C语言里0表示假非0表示真。 -1是真还是假答案真。因为不是0。 5. 分支语句选择结构 5.1 if语句 5.1.1 语法形式 初学者建议按照以下语法形式来写不建议省略大括号。 // 单分支无else if (表达式) {语句列表; }// 单分支有else if (表达式) {语句列表1; } else {语句列表2 }// 多分支 if (表达式1) {语句列表1; } else if (表达式2) {语句列表2; } else {语句列表3 }语句列表可以是1条或多条语句。 当表达式为真时执行if后面大括号内的语句列表否则执行else后面大括号内的语句列表。 如果语句列表里只有1条语句则大括号可以省略。 举例子 #include stdio.hint main() {int age 0;scanf(%d, age);// 单分支无elseif (age 18){printf(未成年\n);printf(好好学习\n);}// 单分支有elseif (age 18){printf(成年\n);}else{printf(未成年\n);printf(好好学习\n);}// 多分支if (age 18){printf(青少年\n);printf(好好学习\n);}else if (age 18 age 30){printf(青年\n);}else if (age 30 age 50){printf(中年\n);}else if (age 50 age 80){printf(中老年\n);}else if (age 80 age 100){printf(老年\n);}else{printf(老寿星\n);}// 省略大括号if (age 18)printf(未成年\n);elseprintf(成年\n);return 0; }5.1.2 else和if的匹配 以下代码执行结果是什么 #include stdio.hint main() {int a 0;int b 2;if (a 1)if (b 2)printf(hehe\n);elseprintf(haha\n);return 0; }要回答这个问题首先要知道else和if是怎么匹配的 else是和对齐的if匹配的吗错请牢牢记住 就近原则else是和最近的if匹配的。 所以上面的代码中的else看似和if(a1)对齐实则是和if(b2)匹配的。也就是说代码应该这样看 #include stdio.hint main() {int a 0;int b 2;if (a 1)if (b 2)printf(hehe\n);elseprintf(haha\n);return 0; }如果还是不清楚再把大括号加上 #include stdio.hint main() {int a 0;int b 2;if (a 1){if (b 2){printf(hehe\n);}else{printf(haha\n);}}return 0; }明白了吧由于a是0if(a1)压根就不会进去所以下面的if和else都不会执行最终结果什么都不会输出。 所以代码风格很重要对于初学者建议没事不要乱省略括号否则可能会写出一些奇奇怪怪的问题。 那么关于if语句有哪些需要注意的代码风格呢 5.1.3 代码风格 下面两种写法意思是不是相同的呢 // 写法1 int test(int flag) {if (flag)return 1;return 0; }// 写法2 int test(int flag) {if (flag){return 1;}else{return 0;} }意思是完全相同的在写法1中如果flag为真就返回1没有机会返回0如果flag为假返回1不会执行自然就返回0了。这和写法2效果完全相同但是写法2明显可读性更强。 再比较下面一组代码 int main() {int a 1;// 写法1if (a 5){printf(true\n);}// 写法2if (5 a){printf(true\n);}return 0; }仍然是意思相同的一组代码但是写法2更好。为什么呢理由如下 如果不小心把写成了第一种写法就变成了if(a5) 这种写法是不会报错的甚至还会输出true。因为a5是一个赋值表达式把5赋值给a这个表达式的值就是a的值即5。5为真就会执行printf。此时程序出错了但是仍然能够运行我们还要去代码中找哪里写错了这就得不偿失了。 如何避免这种情况呢 建议判断变量和常量是否相等时把常量写在左边。 当我们写成if(5a)时如果把写成即if(5a)编译器会直接报错因为这种写法是把一个变量赋值给常量5但常量是不能修改的这在语法上就已经错的没边了。 把常量写在左边哪怕漏掉了一个等号也能根据错误信息很快发现错误的位置而不是去代码中找bug找半天。 5.2 switch语句 5.2.1 语法形式 switch (整型表达式) {语句项; }语句项又是什么呢 // 是一些case语句 // 如下 case 整型常量表达式:语句;举个例子实现这样一个功能输入1输出星期一输入2输出星期二……输入7输出星期天。 错误示范 #include stdio.hint main() {int day 0;scanf(%d, day);switch (day){case 1:printf(星期一\n);case 2:printf(星期二\n);case 3:printf(星期三\n);case 4:printf(星期四\n);case 5:printf(星期五\n);case 6:printf(星期六\n);case 7:printf(星期天\n);}return 0; }这么写有什么问题呢对于上面的程序输入1会输出星期一到星期天。这是因为case只能决定switch语句从哪里进去也就是说当day是1时就会从case 1进去打印星期一。接下来代码会继续往下执行打印星期二星期三直到打印星期天后switch语句才结束。 case语句决定switch语句的入口。 但我们只想输出星期一呀。这就要break语句出场了。 5.2.2 break语句 break语句用于跳出switch语句决定switch语句的出口。 正确的写法是 #include stdio.hint main() {int day 0;scanf(%d, day);switch (day){case 1:printf(星期一\n);break;case 2:printf(星期二\n);break;case 3:printf(星期三\n);break;case 4:printf(星期四\n);break;case 5:printf(星期五\n);break;case 6:printf(星期六\n);break;case 7:printf(星期天\n);break;}return 0; }此时假设我们输入3由于case决定入口程序就会直接跳到case 3处打印星期三接下来遇到了breakbreak决定出口程序就跳出switch语句了。 注意 switch语句后面的括号里必须是整型表达式。 如上面代码中的day是int类型的。 case后面跟的必须是整型常量表达式。 如上面代码中的1 2 3等等。 牢牢记住case决定入口break决定出口。如果没有遇到出口代码就会继续往下执行直到遇到break或者switch语句结束。 练习输入1~5时输出工作日输入6和7时输出休息日。 #include stdio.hint main() {int day 0;scanf(%d, day);switch (day){case 1:case 2:case 3:case 4:case 5:printf(工作日\n);break;case 6:case 7:printf(休息日\n);break;}return 0; }建议在最后一个case语句后加上break虽然哪怕不加switch语句也结束了。 对于上面这段代码如果不在case 7后面加break以后在case 7后面写case 8,case 9等等会导致case语句中功能的重叠。 5.2.3 default语句 还是上面的代码如果输入8程序不会有任何输出因为没有一个case语句能够匹配。 如果我们的需求是没有case能够匹配的情况下输出输入错误又应该怎么写呢 这就需要default语句出场了。 当没有任何case语句能够匹配时会执行default标签下的语句。 在满足要求的情况下default语句的位置是任意的。但是建议放在switch语句的最后处理完正常情况再处理异常情况。 #include stdio.hint main() {int day 0;scanf(%d, day);switch (day){case 1:case 2:case 3:case 4:case 5:printf(工作日\n);break;case 6:case 7:printf(休息日\n);break;default:printf(输入错误\n);break;}return 0; }建议在switch语句的最后都加上default并且在default后面加上break哪怕不做任何处理否则可能会被误认为没有处理异常情况。 6. 循环语句 6.1 while循环 6.1.1 语法形式 对于所有循环的语法形式有一条建议 任何情况下都请不要省略大括号哪怕你已经熟练了否则可读性会非常差。 我见过一些同学写循环代码经常省略大括号看的是真的难受。写代码不要只想着炫技你的代码最终是写给人看的。 明白了这一点来看看while循环的语法形式 while (表达式) {语句列表; }语句列表可以是1条或多条语句。当只有1条语句时大括号可以省略但是不建议省略 如果表达式为真则进入循环否则不进入。 进入循环后会执行语句列表如果没有遇到转向语句如break或continue等等后面会讲就一直执行完大括号内的语句接着再次判断表达式的真假如果为真就再次执行语句列表为假就跳出循环以此类推。 比如打印1~10 #include stdio.hint main() {int i 1;while (i 10){printf(%d , i);i;}return 0; }代码是怎么执行的呢先创建i并初始化为1判断i是否10由于110会进入循环接着打印i即1i自增变成2。再次判断i是否10由于210继续打印i即2i自增变成3……直到i变成10判断i是否10由于1010继续打印i即10i自增变成11再次判断i是否10由于1110为假跳出循环。屏幕上就打印出了1~10。 6.1.2 break语句 break语句用于永久的终止循环。当while循环体内遇到break语句会直接跳出循环。 下面代码的执行结果是什么 #include stdio.hint main() {int i 1;while (i 10){ if (5 i)break;printf(%d , i);i;}return 0; }答案i为1234时进入循环由于5i均为假不会执行break屏幕上打印1 2 3 4接着i为5时进入循环5i为真执行break直接跳出循环。 6.1.3 continue语句 continue用于终止本次循环。当while循环体内遇到continue语句会跳过本次循环后面的代码直接进行下一次循环的入口判断。 #include stdio.hint main() {int i 1;while (i 10){ if (5 i)continue;printf(%d , i);i;}return 0; }对于这段代码i为1 2 3 4时正常打印i为5时5i为真执行continue就跳过了本次循环后面的打印和自增再次来到判断i10由于i仍然是5i10为真再次进入循环体判断5i仍然为真continue后仍然跳过了本次循环后面的打印和自增来到循环的判断此时i还是5。由于每次都会跳过i的自增i永远没有机会变成6也就造成了死循环。 6.1.4 代码举例 下面代码是什么意思呢 #include stdio.hint main() {int ch 0;while ((ch getchar()) ! EOF){putchar(ch);}return 0; }循环每次会从键盘读取一个字符放到ch中去如果读取成功即getchar返回的不是EOF就把这个字符打印出来。如果读取失败getchar返回EOF就跳出循环。 假设有一个场景输入密码然后输入Y确认。下面这段代码有没有什么问题呢 #include stdio.hint main() {char passwd[20] {0};printf(请输入密码:);scanf(%s, passwd);printf(请确认密码(Y/N)\n);int ch getchar();if (Y ch)printf(确认成功\n);elseprintf(确认失败\n);return 0; }如果运行这段代码输入123456并敲回车效果是这样的 并没有等你输入Y或N就直接显示确认失败。这是为什么呢 因为scanf和getchar读取数据时不是从键盘中直接读取的而是从缓冲区中读取的。当我们输入123456并敲回车时缓冲区中就有了123456\n其中\n是由于你敲了个回车而这个回车会触发scanf读取数据scanf会把缓冲区中前面的123456拿走放到数组passwd里此时缓冲区里还剩一个\n没有处理。接着getchar读取数据时发现缓冲区里还有一个\n就二话不说把\n拿走了放到了ch里。接着if语句判断ch是\n呀与Y不相同于是就走了else打印出确认失败。这就是为什么你还没有输入Y或N就直接打印确认失败了。 如何解决这个问题呢这就需要在输入Y或N前清理缓冲区。前面的写法中由于处理不掉123456\n中的最后一个\n而出问题那只需要加一个getchar处理掉最后这个\n不就行了吗。 #include stdio.hint main() {char passwd[20] {0};printf(请输入密码:);scanf(%s, passwd);// 处理掉\ngetchar();printf(请确认密码(Y/N)\n);int ch getchar();if (Y ch)printf(确认成功\n);elseprintf(确认失败\n);return 0; }这样看似没问题了实际上还存在问题。 如果我们输入123456按空格再输入abcdef接着敲回车仍然会出现还没输入Y或N就确认失败的情况。 这又是为什么呢其实这是由于清理缓冲区的力度不够。 在我们输入后缓冲区里就有了123456 abcdefscanf配合%s读取字符串时会拿走空格之前的123456也就是说此时缓冲区中仍然有很多字符用一个getchar来处理是不够的应该用多个getchar这就需要循环出场啦。 最合理的写法是 #include stdio.hint main() {char passwd[20] {0};printf(请输入密码:);scanf(%s, passwd);// 清理缓冲区int tmp 0;while ((tmp getchar()) ! \n){;}printf(请确认密码(Y/N)\n);int ch getchar();if (Y ch)printf(确认成功\n);elseprintf(确认失败\n);return 0; }由于第一次输入时缓冲区的最后一定是个\n只需要一直用getchar读取字符直到读取到\n才停止。这样就完美解决了前面的问题。 6.2 for循环 6.2.1 语法形式 for (初始化语句; 判断表达式; 调整语句) {语句列表; }语句列表可以是一条或多条语句如果是一条语句可以省略大括号但是不建议省略大括号。 初始化语句负责初始化循环变量。 判断表达式负责判断是否进行下一轮循环。 调整语句负责在一次循环结束后调整循环变量。 实际执行时会先执行初始化语句接着看判断表达式的真假如果为假就不进入循环如果为真就进入循环。若进入循环执行完语句列表后会接着执行调整语句然后看判断表达式的真假如果为假就跳出循环如果为真则继续下一轮循环执行循环体内的语句列表以此类推。 比如我们要在屏幕上打印1~10。 #include stdio.hint main() {int i 0;for (i1; i10; i){printf(%d , i);}return 0; }这段代码会先创建i并初始化为0接着执行for循环先把i赋值为1判断i10为真进入循环打印i接着来到ii变成2判断i10为真进入循环打印i接着来到ii变成3判断i10为真进入循环打印i……i变成10判断i10为真进入循环打印i接着来到ii变成11判断i10为假跳出循环。请好好体会一下这一个过程。 6.2.2 break语句 break用于永久终止循环。当for循环体内遇到break会直接跳出循环。 #include stdio.hint main() {int i 0;for (i1; i10; i){if (5 i)break;printf(%d , i);}return 0; }上面的代码当i变成5时判断5i为真执行break会直接跳出循环。输出结果是屏幕上打印1 2 3 4 6.2.3 continue语句 continue语句用于终止本次循环。当for循环体内遇到continue语句会直接跳过本次循环后面的代码来到循环的调整部分。 #include stdio.hint main() {int i 0;for (i1; i10; i){if (5 i)continue;printf(%d , i);}return 0; }上面的代码中当i变成5时5i为真执行continue会直接跳到ii变成6接着打印6 7 8 9 10。输出结果是1 2 3 4 6 7 8 9 10 6.2.4 for循环控制循环变量 建议 1.不可在for循环体内修改循环变量防止for循环失去控制。 2.建议for循环控制变量的取值采用“前闭后开”的写法。 采取前闭后开的写法时可读性更强。如 // 前闭后开的写法 [0, 10) for (i0; i10; i) {}// 两边都是闭区间 [0, 9] for (i0; i9; i) {}上面的写法能够一眼看出会循环10次。 一个经典的例子是打印数组 #include stdio.hint main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int i 0;for (i 0; i 10; i) // 这里采取前闭后开的写法{printf(%d , arr[i]);}return 0; }6.2.5 for循环的变种 for循环的初始化判断调整部分都是可以省略的但是不建议初学者省略。如果省略判断部分则判断部分恒为真。 for (;;) {}下面的代码执行结果是多少 #include stdio.hint main() {int count 0;int i 0;int j 0;for (i 0; i 10; i){for (j 0; j 10; j){count;}}printf(count %d\n, count);return 0; }很简单外层循环10次内层循环10次最后count100。 那如果省略掉初始化呢 #include stdio.hint main() {int count 0;int i 0;int j 0;for (; i 10; i){for (; j 10; j){count;}}printf(count %d\n, count);return 0; }当i为1时i10成立j从0变到9都满足j10会使count自增j变成10后不满足j10跳出内层循环i变成2后i10成立此时j还是10不满足j10不会进入内层循环接着i变成3 4 5 6 7 8 9j都是10都不会进入内层循环所以最后count10。 我们还可以使用多个变量控制循环如 int x 0; int y 0; for (x0, y0; x2 y5; x, y) {}6.3 do while循环 6.3.1 语法形式 do {语句列表; } while (判断表达式);语句列表是一条或多条语句。如果只有一条语句则大括号可以省略但不建议省略。 do while循环中会不管三七二十一先执行大括号里的语句列表接着根据判断表达式的真假性决定是否再次执行语句列表。 由于do while无论如何都会执行一次语句列表一般很少使用。就使用频率来说for循环最多其次是while循环do while循环最少。 举个例子打印1~10。 #include stdio.hint main() {int i 1;do{printf(%d , i);i;} while (i 10);return 0; }循环会不管三七二十一先打印i接着i自增变成2然后判断i10为真继续打印自增变成3再判断i10仍然为真打印自增变成4……自增变成10判断i10为真打印自增变成11i10为假跳出循环。屏幕上就打印了1~10。 6.3.2 break语句 理解了while循环和for循环的break和continue后do while循环的break和continue就很简单了基本大同小异。 break用于永久的终止循环。当do while循环体内遇到break语句会直接跳出循环。 #include stdio.hint main() {int i 1;do{if (5 i)break;printf(%d , i);i;} while (i 10);return 0; }上面这段代码当i为5时遇到break没来得及打印直接跳出循环此时屏幕上打印了1~4。 6.3.3 continue语句 continue用于终止本次循环。当do while循环体内遇到continue会跳过本次循环后面的代码直接来到循环的判断部分。 #include stdio.hint main() {int i 1;do{if (5 i)continue;printf(%d , i);i;} while (i 10);return 0; }先打印1~4当i为5时遇到continue直接跳到判断i10为真继续执行循环体又遇到continue跳到判断仍为真因为每次都跳过了i的自增所以i永远是5就死循环了。 7. goto语句 C语言中提供了可以随意滥用的goto语句和标记跳转的标号。 从理论上goto语句是没有必要的实践中没有goto语句也可以很容易的写出代码。 7.1 语法形式 goto flag; // flag名字可以任意// 执行完goto就会直接跳到这里 flag: // 有可能在goto的前面也有可能在后面但是不能跨函数某些场合下goto语句还是用得着的最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。 如一次跳出两层或多层循环。 多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层循环。 goto语句真正适用的场景如下 for (...) {for (...){for (...){if (disaster)goto error;}} } ... error: if (disaster) {// 处理错误情况 }7.2 代码举例 下面我们写一个关机程序。 只要程序运行起来电脑就在1分钟内关机如果输入我是猪就取消关机。 首先我们要知道如何关机或者取消关机。 设置1分钟后关机的代码system(shutdown -s -t 60); 取消关机任务的代码system(shutdown -a); 需要注意system是一个库函数是用来执行系统命令的。对应的头文件是stdlib.h。 还需要注意字符串是不能用来比较是否相等的需要用的strcmp函数。 完整实现的代码如下 #include stdio.h #include stdlib.h #include string.hint main() {char input[20] { 0 };system(shutdown -s -t 60); again:printf(请注意你的电脑在1分钟内关机如果输入我是猪就取消关机\n);scanf(%s, input);if (strcmp(input, 我是猪) 0){system(shutdown -a);}else{goto again;}return 0; }但是我们可以很简单地用循环实现同样的功能。 #include stdio.h #include stdlib.h #include string.hint main() {char input[20] { 0 };system(shutdown -s -t 60);while (1){printf(请注意你的电脑在1分钟内关机如果输入我是猪就取消关机\n);scanf(%s, input);if (strcmp(input, 我是猪) 0){system(shutdown -a);break;}}return 0; }你可以把这个代码编译生成的可执行程序发给你的好朋友玩玩。需要注意的是编译前记得把模式从debug改成release否则你好朋友的电脑的环境可能运行不了。 8. 综合练习 8.1 打印1~100之间的奇数 我们如何判断一个数是不是奇数呢奇数÷2后会余1即如果n是奇数则n%21 #include stdio.hint main() {int n 0;scanf(%d, n);// 判断n是不是奇数if (n%2 1){printf(%d是奇数\n, n);}return 0; }解题思路产生1~100之间的数如果是奇数就打印。 #include stdio.hint main() {int i 0;// 产生1~100之间的数for (i1; i100; i){// 判断i是不是奇数是奇数就打印if (i%2 1){printf(%d , i);}}return 0; }就这题而言有个更简单的解法。1~100之间的奇数不就是1 3 5 7 9...这是个等差数列公差为2循环调整部分每次加2不就行了。 #include stdio.hint main() {int i 0;for (i1; i100; i2){printf(%d , i);}return 0; }所以说掌握了核心技术写起代码就是爽 8.2 求n的阶乘 解题思路产生1~n的数再乘起来即可。 #include stdio.hint main() {int n 0;scanf(%d, n);int i 0;int ret 1;for (i 1; i n; i){ret * i;}printf(%d\n, ret);return 0; }衍生问题求1!2!3!...10!。 解题思路用n产生1~10每次求n的阶乘并加起来。 #include stdio.hint main() {int n 0;int sum 0;for (n 1; n 10; n){int i 0;int ret 1; // 这里ret必须创建在外层循环里面for (i 1; i n; i){ret * i;}sum ret;}printf(sun %d\n, sum);return 0; }n为1时内层循环求1的阶乘加到sum上n为2时内层循环求2的阶乘加到sum上n为3时内层循环求3的阶乘加到sum上。由于n会从1变到10就求出了1~10的阶乘的和。 这种写法有一个错误的版本如果把ret放在外层循环的外面求出的结果就是错的你知道为什么吗 #include stdio.hint main() {int n 0;int sum 0;int ret 1; // 创建在外层循环外面for (n 1; n 10; n){int i 0;for (i 1; i n; i){ret * i;}sum ret;}printf(sun %d\n, sum);return 0; }这是因为每次ret并不会重置为1这就导致每次求n的阶乘时不是从1开始乘到n而是从n-1的阶乘开始乘上1到n。举个例子就明白了。当n为2时内层循环会算出2的阶乘保存到ret上接着n变成3仔细看代码接下来做的事情是产生1到3并乘到ret上去但是ret是定义在外层循环外面的一次外层循环后ret并没有重新创建此时ret仍然还是前面求出来的2的阶乘所以实际上做的事情是在2的阶乘上乘上1到3这就不是3的阶乘了正确的算法应该是在1上乘上1到3仔细体会这两者的区别。 那为什么前面那种写法就是对的呢因为ret是创建在外层循环里面的。一次外层循环结束后ret会被重新创建并初始化为1就不会保留上一次循环留下来的值了。当然如果想把ret创建在外层循环外面也是可以的但要在循环里面对其进行初始化。 #include stdio.hint main() {int n 0;int sum 0;int ret 1;for (n 1; n 10; n){ret 1; // 要把ret重置为1int i 0;for (i 1; i n; i){ret * i;}sum ret;}printf(sun %d\n, sum);return 0; }不过把ret创建在外头也是有好处的。每次保留下来上一次循环求出来的阶乘也就是说保留的是n-1的阶乘我们算n的阶乘直接在n-1的阶乘上乘以n不就行了吗。 改进后的代码如下 #include stdio.hint main() {int n 0;int sum 0;int ret 1;for (n 1; n 10; n){ret * n;sum ret;}printf(sun %d\n, sum);return 0; }这么写是不是简单多了原来是两层循环这样写只有一层循环代码的效率就提升了不少。学习了时间复杂度你就会明白后一种写法把时间复杂度从O(N2)优化到了O(N)。 8.3 二分查找折半查找 问题如何在一个有序数组中查找具体的某个数字 你可能会想遍历一下不就行了嘛。 #include stdio.hint main() {int arr[] {1,2,3,4,5,6,7,8,9,10};int sz sizeof(arr) / sizeof(arr[0]);int k 7;// 查找kint i 0;for (i0; isz; i){if (arr[i] k){printf(找到了下标是%d\n, i);break;}}if (i sz){printf(找不到了\n);}return 0; }但是这种写法就完全忽略了“有序”两个字因为无序数组也能够这么查找。 这里介绍一个非常厉害的算法二分查找也叫折半查找。 二分查找折半查找我们在一个有序数组里查找一个确定的数每次可以先找到中间那个数如果要查找的数比中间的数大那么就去右边找反之去左边找直到找到为止。 比如一个数组存储的数据是[1 2 3 4 5 6 7 8 9 10]假设要查找7。数组的左下标是0右下标是9计算(09)/2得4以4为下标的数是5由于要查找的7比5大说明要在5的右边找要查找的下标的范围就从[0,9]缩小为[5,9]其中541。接着计算[5,9]的中间下标(59)/27以7为下标的数是8由于要查找的7比8小说明要在8的左边找要查找的下标的范围就从[5,9]缩小为[5,6]其中67-1。接着计算[5,6]的中间下标(56)/25以5为下标的数是6由于要查找的7比6大说明要在6的右边找要查找的下标的范围就从[5,6]缩小为[6,6]其中651。接着计算[6,6]的中间下标(66)/26以6为下标的数是7和要查找的7相等就找到啦。由于[6,6]范围内只有一个数据如果这一个数据都和要查找的数据不相等那就找不到了。 明白了二分查找的思路我们还要把它转换成代码。由于逻辑较为复杂我会一段一段写并加以分析。 首先创建数组int arr[] {1,2,3,4,5,6,7,8,9,10}; 定义要查找的元素假设是7int k 7; 由于我们每次要用中间元素的下标跟k比较我们需要左下标和右下标才能计算中间元素的下标其中一开始左下标是数组的左下标即0右下标是数组的元素个数假设是sz减1。 int left 0; int right sz - 1;但我们不知道数组的元素个数呀所以需要计算。int sz sizeof(arr) / sizeof(arr[0]); 接着我们需要进行二分查找。二分查找可能需要进行多次这就有了循环的可能。不过目前我们先写一次查找。 计算中间元素的下标即左下标和右下标的平均值。 int mid (leftright) / 2;这么计算有一个问题如果left和right比较大加起来后有可能导致越界那怎么办呢有朋友可能说了运用结合律这么写int mid left/2 right/2;但是这么写的话如果left是3right是53和5的平均值应该是4但是3/25/2123那就出问题了。所以最好的写法是int mid left (right-left) / 2; 这种写法是如何想出来的呢请你闭上眼睛想象一下有两条绳子一长一短我们如何能让两条绳子一样长呢只需要比一比长的那条绳子比短的那条绳子多出来多少把多的那部分平分为两半在补给短的绳子就行了这就是上面这行代码的由来。 有了中间元素的下标我们还需要和要查找的k比较才知道是去左边找还是去右边找。 if (arr[mid] k) {} else if (arr[mid] k) {} else {}这分别代表哪三种情况呢如果中间的元素比k要小说明k在右边那要查找的范围就变成了原来范围的右半边。原来的范围是[left,right]那现在的范围是什么呢有朋友会说是[mid,right]但是事实上是[mid1,right]因为arr[mid]已经比k要小了自然就不用查找了。也就是说原来范围的right不变left要变成mid1。同理如果中间的元素比k要大说明k在左边原来的left不变right要变成mid-1。大于小于都讨论了剩下的就是等于了那就找到了由于此时arr[mid]k也就是说找到的下标就是mid。 if (arr[mid] k) {left mid 1; } else if (arr[mid] k) {right mid - 1; } else {printf(找到了下标是%d, mid); }但是不一定找一次就能找到呀可能会找很多次这就需要一个循环。这里用while循环会比较合适。循环的条件是什么呢二分查找的过程中每次都会砍掉一半体现在代码上left和right会逐渐逼近但是left会始终在right的左边也就是leftright而当leftright时区间还有一个元素可以查找如果这一个元素都不是我们能要找的元素那就找不到了。所以循环的条件是leftright。还有一点要注意要在循环里面计算mid因为每次都需要一个新的mid。当我们找到了就没必要继续找了直接break出去。 while (left right) {int mid left (right - left) / 2;if (arr[mid] k){left mid 1;}else if (arr[mid] k){right mid - 1;}else{printf(找到了下标是%d\n, mid);break;} }当循环结束有两种可能第一种是找到之后break出去此时一定有leftright另一种是找呀找呀发现找不到此时已经不满足leftright了即满足leftright才跳出循环。所以循环结束后还要加一个是否找到的判断。 if (left right) {printf(找不到了\n); }完整的代码如下 #include stdio.hint main() {int arr[] { 1,2,3,4,5,6,7,8,9,10 };int k 7;// 查找kint sz sizeof(arr) / sizeof(arr[0]);int left 0;int right sz - 1;while (left right){int mid left (right - left) / 2;if (arr[mid] k){left mid 1;}else if (arr[mid] k){right mid - 1;}else{printf(找到了下标是%d\n, mid);break;}}if (left right){printf(找不到了\n);}return 0; }8.4 字符串从两端向中间打印 假设有一个字符串Hello, World!!!!! 我们想一开始在屏幕打印一串#再逐渐从两端向中间展示这个字符串。 思路可以定义两个数组一个数组存储Hello,World!!!!!另一个数组存储一串#每次把前者两端的字符拿到后者中去并打印后者。每次拿完后暂停1秒再清屏。 #include stdio.h #include string.h #include Windows.h #include stdlib.hint main() {char arr1[] Hello, World!!!!!;char arr2[] #################;int left 0;int right strlen(arr1) - 1;while (left right){arr2[left] arr1[left];arr2[right] arr1[right];printf(%s\n, arr2);Sleep(1000);system(cls);left;--right;}printf(%s\n, arr2);return 0; }8.5 模拟登陆情景 输入密码假设密码是123456若输入错误3次则退出程序。 注意判断密码是否正确的代码应使用strcmp函数不能直接使用比较字符串。 #include stdio.h #include string.hint main() {char passwd[20] { 0 };int i 0;for (i 0; i 3; i){printf(请输入密码:);scanf(%s, passwd);if (strcmp(passwd, 123456) 0){printf(密码正确\n);break;}else{printf(密码错误重新输入\n);}}if (3 i){printf(三次密码均输入错误退出程序\n);}return 0; }8.6 猜数字游戏 首先我们需要学会如何使用C语言生成一个随机数。 使用rand函数生成一个0~32767的随机数。使用rand函数不需要传参数。rand函数会直接返回生成的随机数。rand函数对应的头文件是stdlib.h。 调用rand函数之前需要调用srand函数来设置随机数生成器的起点srand函数只能调用一次。srand函数需要传一个unsigned int类型的参数该参数是一个随机数一般建议传时间戳。srand函数对应的头文件也是stdlib.h。 使用time函数生成一个时间戳。如果你不想保存这个时间戳只需要给time函数传一个空指针NULL即可。time函数会返回对应的时间戳类型是time_t。 如果要把time函数返回的时间戳传给srand需要强制类型转换成unsigned int。 写一个演示代码来生成随机数。 #include stdio.h #include stdlib.h #include time.hint main() {srand((unsigned int)time(NULL));int ret rand();printf(ret %d\n, ret);return 0; }回归正题。猜数字游戏的要求是 电脑随机生成一个数字1~100。玩家猜数字。玩家猜小了就告知猜小了。玩家猜大了就告知猜大了。直到猜对为止。游戏可以一直玩。 对于第一点我们可以写int ret rand() % 100 1;因为%100之后得到0~99再1即可得到1~100。 对于第二点只需要一个while循环配合if语句。 对于第三点可以使用do while循环。因为这个游戏至少要进去一次使用do while循环最合适。 参考代码如下 #include stdio.h #include time.h #include stdlib.hvoid menu() {printf(************************\n);printf(***** 1. play *****\n);printf(***** 0. exit *****\n);printf(************************\n); }void game() {int ret rand() % 100 1;int guess 0;while (1){printf(猜数字:);scanf(%d, guess);if (guess ret){printf(猜大了\n);}else if (guess ret){printf(猜小了\n);}else{printf(恭喜你猜对了\n);break;}} }int main() {int input 0;srand((unsigned int)time(NULL));do{menu();printf(请选择:);scanf(%d, input);switch (input){case 1:game();break;case 0:printf(退出游戏\n);break;default:printf(选择错误重新选择\n);break;}} while (input);return 0; }注意 srand函数在一个程序中只能被调用一次。就上面的代码而言srand函数是写在main函数里的所以只会被调用一次。int ret rand() % 100 1;必须放在while循环外面否则每次循环都会生成一个新的随机数。猜大了和猜小了的提示不要搞反了 8.7 把三个整数从大到小输出 假设我有三个数如何从大到小输出呢 解题思路假设三个数是a,b,c我输出总得有一个顺序吧比如先打印a接着打印b再打印c。那是不是就是要把最大的放a里中间的数放b里最小的放c里 我们可以先比较a和b如果a比b大那就是我们想要的如果a比b小那就交换a和b这样就把a和b中较大的数放在了a里了。同理再把a和c中较大的放在a里但个数中最大的就放在a里了。然后比较b和c把大的放b里小的放c里就行了。 那如何交换两个变量呢假设交换a和b只需要创建一个临时变量把a放临时变量里再把b放在a里最后把临时变量中原来a的值放在b里就行了。 int tmp a; a b; b tmp;完整的代码如下 #include stdio.hint main() {int a 0;int b 0;int c 0;scanf(%d %d %d, a, b, c);if (a b){int tmp a;a b;b tmp;}if (a c){int tmp a;a c;c tmp;}if (b c){int tmp b;b c;c tmp;}printf(%d %d %d\n, a, b, c);return 0; }8.8 求最大公约数 如何求两个数的最大公约数呢 比如24和18的最大公约数怎么求呢 可以先找到小的那个数即18最大公约数不可能比18大所以我们就挨个挨个地试18是不是最大公约数17是不是16呢15呢直到试出来为止。 #include stdio.hint main() {int a 0;int b 0;scanf(%d %d, a, b);// 找出较小值int m a b ? a : b;while (1){if (a % m 0 b % m 0)break;--m;}printf(%d\n, m);return 0; }不过这种方法效率太低了。有一种厉害的算法辗转相除法可以更高效地解决这个问题。 简单来说辗转相除法就是反复地取模操作符是%。求a和b的最大公约数只需要先a模b得到c如果c不是0就再b模c得到d如果d不是0就d模e得到f……假设后面一直取模当x模y刚好得到0那么y就是最大公约数。 这种解法不需要先比较大小。如果a比b大自然可以一直取模。如果a比b小假设a是18b是24那么a模b得到c即18再b模c时此时就是24模18这和一开始就是24和18的效果是一样的。 如何实现辗转相除法呢可以使用while循环每次计算a模b的值如果是0那么b就是最大公约数。如果不是0呢就需要把模出来的值记录下来记为m然后把b的值给a把m的值给b此时再算a模b就是在算b模m了这样就能一直循环下去了。当a模b得到0时跳出循环此时b就是最大公约数。 #include stdio.hint main() {int a 0;int b 0;int m 0;scanf(%d %d, a, b);while (m a % b){a b;b m;}printf(%d\n, b);return 0; }当然我们可以用函数来实现。 #include stdio.hint get_greatest_common_divisor(int x, int y) {int m 0;while (m x % y){x y;y m;}return y; }int main() {int a 0;int b 0;scanf(%d %d, a, b);int ret get_greatest_common_divisor(a, b);printf(ret %d\n, ret);return 0; }8.9 求最小公倍数 和最大公约数大同小异。 解题思路假设求18和24的最小公倍数。较大的数是24最小公倍数不可能比24小所以就一个一个试。24是不是最小公倍数25是不是26呢直到试对为止。 #include stdio.hint main() {int a 0;int b 0;scanf(%d %d, a, b);// 求较大值int m a b ? a : b;while (1){if (m % a 0 m % b 0)break;m;}printf(%d\n, m);return 0; }当然一个一个去试也太慢了。最小公倍数一定是其中一个数的整数倍。比如a和b的最小公倍数一定是a的整数倍那就只需要试a2×a3×a……就行了。 #include stdio.hint main() {int a 0;int b 0;int m 0;scanf(%d %d, a, b);for (m a; m % b ! 0; m a){;}printf(%d\n, m);return 0; }除此之外我们还可以这么做 假设求a和b的最小公倍数只需要算a×b÷(a和b的最大公约数)。这就可以复用上面写的函数了。 #include stdio.h// 最大公约数 int get_greatest_common_divisor(int x, int y) {int m 0;while (m x % y){x y;y m;}return y; }// 最小公倍数 int get_least_common_multiple(int x, int y) {int m get_greatest_common_divisor(x, y);return x * y / m; }int main() {int a 0;int b 0;scanf(%d %d, a, b);int ret get_least_common_multiple(a, b);printf(%d\n, ret);return 0; }8.10 打印闰年 题目打印1000~2000之间的闰年。 解题思路只需要用for循环产生1000到2000是闰年就打印。 如何判断是不是闰年呢 能被4整除并且不能被100整除是闰年。能被400整除是闰年。 纯用if else来写是这样的 #include stdio.hint main() {int y 0;for (y 1000; y 2000; y){if (y % 4 0){if (y % 100 ! 0){printf(%d , y);}}if (y % 400 0){printf(%d , y);}}return 0; }如果你会用逻辑操作符可以这样写 #include stdio.hint main() {int y 0;for (y 1000; y 2000; y){if (((y % 4 0) (y % 100 ! 0)) || (y % 400 0)){printf(%d , y);}}return 0; }8.11 打印素数 题目打印100~200之间的素数。 解题思路产生100~200之间的数是素数就打印。 什么是素数素数就是质数只能被1和它本身整除的数字。 如何判断一个数字i是不是素数 很简单拿2到i-1的数字去试除i 如果i被整除就说明i不是素数如果2到i-1的数字都不能整除i说明i是素数。 这就需要一个循环产生2到i-1的数字当该循环结束后就全都试除完了此时如何判断是否有整除现象呢我们可以定义一个flag并初始化为1一旦整除就置成0。如果循环结束后flag仍为1就说明没有一个数能够整除就说明i是素数。 int main() {int i 0;for (i 100; i 200; i){int flag 1; // 假设i是素数int j 0;for (j 2; j i; j){if (i % j 0){// i不是素数flag 0;break;}}if (1 flag){// i是素数printf(%d , i);}}return 0; }这是最容易想到的写法还可以做一些优化。 如果一个数ia×b那么a个b中至少有一个数小于或等于i的算术平方根。 比如162×84×416的算术平方根是4这两种写法中都至少有一个数小于或等于4。 所以试除的时候就不需要从2一直试到i-1只需要试到i的算术平方根就行了。 C语言提供了sqrt函数用于开平方使用时需要引用头文件math.h。 还有一个小点可以优化偶数一定不是素数所以只需要判断所有的奇数就行了。 #include stdio.h #include math.hint main() {int i 0;for (i 101; i 200; i 2){int flag 1; // 假设i是素数int j 0;for (j 2; j sqrt(i); j){if (i % j 0){// i不是素数flag 0;break;}}if (1 flag){// i是素数printf(%d , i);}}return 0; }8.12 统计9的个数 题目统计1~100中9的个数。 解题思路要么个位是9要么十位是9。若i个位是9则i%109。若i十位是9则i/109。只需要产生1~100的数再根据上面的两种情况分类讨论就行了。 #include stdio.hint main() {int i 0;int count 0;for (i 1; i 100; i){if (i % 10 9)count;if (i / 10 9)count;}printf(count %d\n, count);return 0; }8.13 分数求和 题目计算1/1-1/21/3-1/4...1/99-1/100。 思路产生1~100的数作为分母再用一个flag变量产生正负交替的效果。 注意对于/操作符如果两个操作数都是整数最终的结果也是整数只有一端是小数算出来的结果才是小数。 #include stdio.hint main() {int i 0;double sum 0;int flag 1;for (i 1; i 100; i){sum sum (1.0 / i) * flag;flag -flag;}printf(sum %lf\n, sum);return 0; }8.14 求10个整数的最大值 思路可以先假设第一个数是最大值接着拿后面9个数和这个数比如果有比假设的最大值大的数就更新最大值。 #include stdio.hint main() {int arr[10] { 0 };int i 0;for (i 0; i 10; i){scanf(%d, arr[i]);}int max arr[0];for (i 1; i 10; i){if (arr[i] max){max arr[i];}}printf(max %d\n, max);return 0; }8.15 打印乘法口诀表 解题思路用变量i产生行数用变量j产生列数。 如果想要打印时能够尽可能对齐可以使用%-2d的格式来打印乘积。其中负号表示左对齐2表示至少打印两位不够就用空格补齐。 #include stdio.hint main() {int i 0;for (i 1; i 9; i){int j 0;for (j 1; j i; j){printf(%d*%d%-2d , i, j, i * j);}printf(\n);}return 0; }
http://www.zqtcl.cn/news/591602/

相关文章:

  • 成都网站开发培训综合网站开发实训总结
  • 个人备案网站名称文学网站开发
  • 花钱做推广广告哪个网站好泗洪网页设计
  • 望牛墩镇网站建设公司网站建设企业熊掌号
  • 做网站一般会出现的问题静态网站的好处就是安全性好从而
  • 做海鲜代理在什么网站建筑网片钢筋网生产厂家
  • 万网网站建设方案书导购网站开发要多少钱
  • 做网站muse好还是DW好用wordpress %link
  • html5门户网站模版做投标需要知道什么网站
  • 合肥网站制作推广seo引擎搜索网站关键词
  • 陕西建设厅网站wordpress 不显示ip
  • 郑州外贸网站制作营销号视频生成器手机版
  • 绵阳市建设工程质量监督站网站wordpress的插件目录
  • 建设和管理环保网站西安做兼职网站设计
  • 在网站中写小说想要删除如何做婚纱摄影的网站怎么做
  • 重庆自适应网站建设wordpress添加icon文件
  • 目前小说网站排名适合网站设计的gif图片
  • 深圳建立网站wordpress 安装语言
  • 南京做中英文网站海南网站建设哪家专业
  • 做网站用jquerywordpress邮件有什么用
  • 上海网站建设免the 7 wordpress
  • 知名建站的公司微信企业app手机下载安装
  • 鹤山做网站羊毛网站建设视频
  • 图书类网站开发的背景建筑培训机构
  • 外贸网站建设制作wordpress管理员页面404
  • 北郊网站建设app网站开发哪里有
  • 像素人物制作网站网站开发的话术
  • 网站关键词怎么优化排名wordpress电子商城模板
  • 电子商务网站建设与维护能赚多少钱成交型网站建设
  • 到国外做网站网站是怎么回事中国一级建造师网官网