)
前置说明此文章仅为个人学习中的周记如有错误敬请谅解。主要内容为郝斌老师的B站课程视频以及学习中遇到的各种问题的补充知识。一、基本的输入和输出函数1.printf—将变量内容输出到屏幕上printf的4种用法1.printf(字符串);2.printf(输出控制符,输出参数);3.printf(输出控制符1 输出控制符2 .......,输出参数1 输出参数2 .......);输出控制符和输出参数的个数必须一一对应4.printf(输出控制符 非输出控制符,输出参数);常用输出控制符控制符含义对应类型%d有符号十进制整数int%o无符号八进制整数unsigned int%x无符号十六进制整数小写unsigned int%X无符号十六进制整数大写unsigned int%f十进制浮点数double%c单个字符int通常传char%s字符串char *%p指针地址void *%%输出一个百分号无参数长度修饰符长度修饰符放在转换字符前面用来匹配更宽或更窄的整数类型。修饰符含义示例hhchar级别%hhd、%hhuhshort级别%hd、%hullong或宽字符%ld、%lu、%lclllong long%lld、%lluzsize_t%zutptrdiff_t%tdjintmax_t%jdLlong double%Lf例c long a 123456789L; printf(%ld\n, a); unsigned long long b 123456789012345ULL; printf(%llu\n, b); long double pi 3.1415926535L; printf(%Lf\n, pi);注意float传给printf时会自动提升为double所以输出float用%f即可。但scanf读float必须用%f读double必须用%lf。宽度、精度和标志1. 宽度宽度指定最少输出多少个字符c printf(%5d\n, 42); // 42 printf(%-5d|\n, 42); // 42 |2. 精度对整数精度表示最少数字位数对字符串表示最多输出多少个字符对浮点数表示小数点后位数。c printf(%.3d\n, 42); // 042 printf(%.2f\n, 3.14159); // 3.14 printf(%.5s\n, hello world); // hello3. 常用标志标志含义-左对齐默认右对齐正数也显示空格正数前加空格负数前加-#八进制加0十六进制加0x0用 0 填充宽度例子c printf(%d\n, 42); // 42 printf(% d\n, 42); // 42 printf(%#x\n, 255); // 0xff printf(%08d\n, 42); // 000000422.scanf——通过键盘将数据输入变量中scanf的两种用法1.scanf(输入控制符,输入参数);将键盘输入的字符转换为输入控制符所规定格式的数据然后存入以输出参数的值为地址的变量中。2.scanf(非输入控制符 输入控制符,输入参数);将键盘输入的字符转换为输入控制符所规定格式的数据然后存入以输出参数的值为地址的变量中非输入控制符必须要原样输入。如果格式串里写了逗号输入时也必须写逗号。c scanf(%d,%d, a, b);正确输入text 10,20错误输入text 10 20因为格式串要求中间必须有一个逗号。再比如c scanf(a%d, a);输入必须是text a100否则读取失败。常见注意事项类型必须匹配%d对应int%f对应double%s对应char *。类型不匹配是未定义行为可能输出乱码甚至崩溃。printf和scanf的%f不同printffloat和double都用%fscanffloat用%fdouble用%lf。%s遇到\0停止字符串必须以\0结尾否则会越界读取。%p建议先转void *c printf(%p\n, (void *)p);二、运算符C 语言运算符简单说就是对数据进行运算的符号。它决定了对几个操作数做什么操作比如加减、比较、赋值、取地址、逻辑判断等。C 的运算符很丰富按功能大致可以分为以下几类。1、算术运算符用于数值计算。运算符含义示例加法a b-减法a - b*乘法a * b/除法a / b%取模求余数a % b自增a、a--自减a--、--a注意整数除法会截断小数5 / 2 2。要得到小数至少一个操作数要是浮点数5.0 / 2 2.5。%只能用于整数、--有前置和后置区别a先加再使用a先使用再加。c int a 5; int b a; // b 5, a 6 int c a; // a 7, c 72、关系运算符用于比较结果是0或1。C 里没有专门的布尔类型时0表示假非0表示真。运算符含义大于小于大于等于小于等于等于!不等于c int a 3, b 5; printf(%d\n, a b); // 1 printf(%d\n, a b); // 0常见坑是赋值才是判断相等。c if (a 5) { } // 错误意图把 5 赋给 a条件为真 if (a 5) { } // 正确判断 a 是否等于 53、逻辑运算符用于逻辑判断。运算符含义说明逻辑与两边都为真才为真||逻辑或一边为真就为真!逻辑非真变假假变真c int a 1, b 0; printf(%d\n, a b); // 0 printf(%d\n, a || b); // 1 printf(%d\n, !a); // 0短路特性左边为假右边不再计算||左边为真右边不再计算。c if (p ! NULL *p 10) { } // 安全p 为空时不会解引用4、赋值运算符运算符含义等价于赋值加后赋值a a b-减后赋值a a - b*乘后赋值a a * b/除后赋值a a / b%取模后赋值a a % b按位与后赋值a a b|按位或后赋值a a | b^按位异或后赋值a a ^ b左移后赋值a a b右移后赋值a a bc int a 10; a 5; // a 15 a * 2; // a 30编程技巧c x | (1 n); // 第 n 位置一 x ~(1 n); // 第 n 位清零 x ^ (1 n); // 第 n 位翻转 int bit (x n) 1;//读取第N位置一用或清零用与取反翻转用异或读取用与加移位。5、位运算符直接对二进制位操作只能用于整数。运算符含义示例按位与a b|按位或a | b^按位异或a ^ b~按位取反~a左移a 1右移a 1例子c int a 5; // 0101 int b 3; // 0011 printf(%d\n, a b); // 0001 1 printf(%d\n, a | b); // 0111 7 printf(%d\n, a ^ b); // 0110 6 printf(%d\n, a 1); // 1010 10 printf(%d\n, a 1); // 0010 2常用技巧x 1判断奇偶x n相当于乘2^nx n相当于除2^nx ^ y可用于交换两个数。6、条件运算符C 里唯一的三目运算符c 条件 ? 表达式1 : 表达式2条件为真取表达式1否则取表达式2。c int a 10, b 20; int max (a b) ? a : b; // max 207、逗号运算符逗号表达式从左到右依次计算整个表达式的值是最后一个表达式的值。c int a (1, 2, 3); // a 3常见于for循环c for (int i 0, j 10; i j; i, j--) { // ... }8、指针与地址运算符运算符含义取地址*解引用[]数组下标.结构体成员访问-结构体指针成员访问c int a 10; int *p a; printf(%d\n, *p); // 10 struct Point { int x, y; }; struct Point pt {1, 2}; struct Point *q pt; printf(%d\n, q-x); // 19、其他运算符运算符含义sizeof求类型或变量占多少字节(type)强制类型转换?:条件运算符,逗号运算符*、指针相关-、.成员访问c printf(%zu\n, sizeof(int)); // 通常 4 double d (double)5 / 2; // 2.5运算符的优先级算术关系逻辑赋值三、流程控制if语句1.最简单用法if(表达式){语句};如果表达式为真则执行语句。条件运算符替代简单 if简单的二选一可以用三目运算符c int max (a b) ? a : b;等价于c int max; if (a b) { max a; } else { max b; }2.if的范围问题1if(表达式)语句A;语句B;if默认只能控制语句A是否执行语句B一定会执行。2if(表达式){语句A;语句B;};此时if可以控制A和B是否执行。3if ...else...c if (条件) { // 条件为真 } else { // 条件为假 }例子c int a 3; if (a % 2 0) { printf(偶数\n); } else { printf(奇数\n); }(4)if...else if...elsec if (条件1) { // 条件1成立 } else if (条件2) { // 条件2成立 } else if (条件3) { // 条件3成立 } else { // 以上都不成立 }例子c int score 85; if (score 90) { printf(优秀\n); } else if (score 80) { printf(良好\n); } else if (score 60) { printf(及格\n); } else { printf(不及格\n); }注意else if是依次判断的一旦某个条件成立后面的分支就不再判断。条件写法条件可以是c if (a) // a 非 0 为真 if (!a) // a 为 0 为真 if (a b) // 比较 if (a b) // 逻辑与 if (a || b) // 逻辑或 if (a 1 b ! 0)表达式常见错误c if (a 5) // 错误这是赋值不是比较 if (a 5) // 正确判断相等为了避免这种错误可以把常量写左边c if (5 a) // 如果少写一个 编译会报错总结if和else的要点if (条件)条件为真执行为假跳过else处理条件为假的情况else if处理多个分支按顺序判断可以嵌套但要注意可读性C 里0为假非0为真是赋值是判断相等不要写错建议始终写大括号避免悬空else问题switch语句switch是 C 语言里另一种分支控制结构适合一个表达式对多个固定值进行判断的场景。它和if...else if功能上有重叠但当分支条件是离散的整数值时switch通常更清晰、更直观。1、基本形式c switch (表达式) { case 常量1: 语句; break; case 常量2: 语句; break; ... default: 语句; break; }例子c int day 3; switch (day) { case 1: printf(星期一\n); break; case 2: printf(星期二\n); break; case 3: printf(星期三\n); break; default: printf(其他\n); break; }2、执行流程计算switch后面表达式的值和各个case后的常量比较匹配到哪个case就从那里开始往下执行遇到break就跳出switch如果都没有匹配执行default分支。注意default不是必须的但建议写上用来处理意外情况。3、break 的作用break用来跳出switch。如果没有break程序会继续执行下一个case这叫穿透。同时break也能跳出上一层循环。c int a 1; switch (a) { case 1: printf(one\n); case 2: printf(two\n); case 3: printf(three\n); break; default: printf(other\n); }输出text one two three因为case 1没有break会一直往下执行到break为止。4、故意利用穿透有时候穿透是故意用的比如多个case共用一段代码c char ch a; switch (ch) { case a: case e: case i: case o: case u: printf(元音\n); break; default: printf(辅音\n); break; }这种写法是合法的也很常见。5、default 的位置default可以放在任意位置但通常放最后。它的执行时机是所有case都不匹配时。c switch (x) { default: printf(未知\n); break; case 1: printf(一\n); break; }虽然语法允许但为了可读性一般还是放最后。6、case 的要求case后面必须是整型常量表达式不能是变量也不能是浮点数。合法c case 1: case A: case 1 2: case sizeof(int):非法c int n 3; case n: // 错误n 是变量 case 1.5: // 错误不是整型 case abc: // 错误不是整型switch表达式也必须是整型或枚举类型不能是浮点、字符串。7、switch 与 if 的对比适合用switchc switch (choice) { case 1: ... break; case 2: ... break; case 3: ... break; default: ... break; }适合用ifc if (score 90) { ... } else if (score 80) { ... } else if (score 60) { ... } else { ... }区别switch适合等值判断且值是离散常量if适合范围判断和复杂条件switch通常比一长串else if更清晰switch只能判断整型、字符、枚举不能判断浮点和字符串。8、常见坑忘记 break导致意外穿透执行了不该执行的分支。case 后是变量ccase n: // 错误在 case 中定义变量C 语言里case后面直接定义变量可能出问题最好用大括号包起来ccase 1: { int x 10; printf(%d\n, x); break; }default 忘记 break虽然放最后时通常不影响但养成加break的习惯更安全。switch 表达式类型不能用float、double、字符串。9、总结switch的要点语法switch (整型表达式) { case 常量: ... break; default: ... }适合对离散整数值做多分支判断break用来跳出不加会穿透穿透可以故意利用让多个case共用代码case后必须是整型常量表达式default处理所有不匹配的情况不能用于浮点数、字符串和范围判断。switch是“对号入座”式的分支if是“条件判断”式的分支。值离散、分支多时用switch条件复杂、范围判断时用if。循环语句1.for循环c for (初始化表达式; 条件表达式; 更新表达式) { 循环体; }执行流程执行初始化表达式只执行一次判断条件表达式为真则执行循环体为假则结束循环执行循环体执行更新表达式回到第 2 步继续判断。c for (int i 0; i 5; i) { printf(%d\n, i); }int i 0初始化只执行一次i 5循环条件每次循环前判断i更新每次循环体执行完后执行。这三个表达式都可以省略此时等价于while(1)但分号不能省。for循环嵌套1.c for(1;2;3)//1 for(4;5;6)//2 A;//3 B;//4整体是两个语句1、2、3是第一个语句4是第二个语句。2.for(1;2;3)//1 for(4;5;6)//2 { A;//3 B;//4 }此时是一个语句。例题输出九九乘法表for (int i 1; i 9; i) { for (int j 1; j i; j) { printf(%d*%d%d , j, i, i * j); } printf(\n); }2.while循环c while (条件表达式) { 循环体; }执行流程判断条件表达式为真执行循环体回到第 1 步继续判断为假结束循环。while 和 for 的关系for可以看成while的紧凑写法c for (int i 0; i 5; i) { printf(%d\n, i); }等价于c int i 0; while (i 5) { printf(%d\n, i); i; }区别for适合次数明确的计数循环while适合条件驱动、次数不确定的循环while的初始化通常在循环外更新在循环体内容易忘记写更新导致死循环。break 和 continuebreak如果用于循环则终止整个循环如果用于switch则是用于终止switchbreak不能直接用于if除非if属于循环内部一个语句。例c for(i0;i3;i) { if(32) break; printf(嘿嘿\n);//永远不会输出 }break虽然是if内部语句但是终止的是外面的for循环。在多层嵌套中break只能终止最里面包裹它的那个循环。在多层 switch中终止距离最近的switch。continue用于跳过本次循环余下的语句转去判断是否需要执行下次循环。例c while(1) { A; B; continue; C; D; }C和D会被跳过不执行。