☰
C语言学习周记(2)
2026/10/2 2:23:57 网站建设 项目流程

前置说明:此文章仅为个人学习中的周记,如有错误敬请谅解。主要内容为郝斌老师的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、%hhu
hshort级别%hd、%hu
llong或宽字符%ld、%lu、%lc
lllong long%lld、%llu
zsize_t%zu
tptrdiff_t%td
jintmax_t%jd
Llong 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"); // hello
3. 常用标志
标志含义
-左对齐,默认右对齐
+正数也显示+
空格正数前加空格,负数前加-
#八进制加0,十六进制加0x
0用 0 填充宽度

例子:

c printf("%+d\n", 42); // +42 printf("% d\n", 42); // 42 printf("%#x\n", 255); // 0xff printf("%08d\n", 42); // 00000042

2.scanf——通过键盘将数据输入变量中

scanf的两种用法:

1.scanf("输入控制符",输入参数);

将键盘输入的字符转换为输入控制符所规定格式的数据,然后存入以输出参数的值为地址的变量中。

2.scanf("非输入控制符 输入控制符",输入参数);

将键盘输入的字符转换为输入控制符所规定格式的数据,然后存入以输出参数的值为地址的变量中,非输入控制符必须要原样输入。

如果格式串里写了逗号,输入时也必须写逗号。

c scanf("%d,%d", &a, &b);

正确输入:

text 10,20

错误输入:

text 10 20

因为格式串要求中间必须有一个逗号。

再比如:

c scanf("a=%d", &a);

输入必须是:

text a=100

否则读取失败。

常见注意事项

  1. 类型必须匹配
    %d对应int,%f对应double,%s对应char *。类型不匹配是未定义行为,可能输出乱码甚至崩溃。

  2. printf和scanf的%f不同

    • printf:float和double都用%f;

    • scanf:float用%f,double用%lf。

  3. %s遇到'\0'停止
    字符串必须以'\0'结尾,否则会越界读取。

  4. %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 = 7

2、关系运算符

用于比较,结果是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 是否等于 5

3、逻辑运算符

用于逻辑判断。

运算符含义说明
&&逻辑与两边都为真才为真
||逻辑或一边为真就为真
!逻辑非真变假,假变真
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 >> b
c 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^n;

  • x >> n相当于除2^n;

  • x ^= y可用于交换两个数。

6、条件运算符

C 里唯一的三目运算符:

c 条件 ? 表达式1 : 表达式2

条件为真取表达式1,否则取表达式2。

c int a = 10, b = 20; int max = (a > b) ? a : b; // max = 20

7、逗号运算符

逗号表达式从左到右依次计算,整个表达式的值是最后一个表达式的值。

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); // 1

9、其他运算符

运算符含义
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的范围问题

(1)

if(表达式)

语句A;

语句B;

if默认只能控制语句A是否执行,语句B一定会执行。

(2)

if(表达式)

{

语句A;

语句B;

};

此时if可以控制A和B是否执行。

(3)if ...else...

c if (条件) { // 条件为真 } else { // 条件为假 }

例子:

c int a = 3; if (a % 2 == 0) { printf("偶数\n"); } else { printf("奇数\n"); }

(4)if...else if...else

c 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、执行流程

  1. 计算switch后面表达式的值;

  2. 和各个case后的常量比较;

  3. 匹配到哪个case,就从那里开始往下执行;

  4. 遇到break就跳出switch;

  5. 如果都没有匹配,执行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 的对比

适合用switch:

c switch (choice) { case 1: ... break; case 2: ... break; case 3: ... break; default: ... break; }

适合用if:

c if (score >= 90) { ... } else if (score >= 80) { ... } else if (score >= 60) { ... } else { ... }

区别:

  • switch适合等值判断,且值是离散常量;

  • if适合范围判断和复杂条件;

  • switch通常比一长串else if更清晰;

  • switch只能判断整型、字符、枚举,不能判断浮点和字符串。

8、常见坑

  1. 忘记 break
    导致意外穿透,执行了不该执行的分支。

  2. case 后是变量

    c

    case n: // 错误
  3. 在 case 中定义变量
    C 语言里,case后面直接定义变量可能出问题,最好用大括号包起来:

    c

    case 1: { int x = 10; printf("%d\n", x); break; }
  4. default 忘记 break
    虽然放最后时通常不影响,但养成加break的习惯更安全。

  5. switch 表达式类型
    不能用float、double、字符串。

9、总结

switch的要点:

  • 语法:switch (整型表达式) { case 常量: ... break; default: ... }

  • 适合对离散整数值做多分支判断;

  • break用来跳出,不加会穿透;

  • 穿透可以故意利用,让多个case共用代码;

  • case后必须是整型常量表达式;

  • default处理所有不匹配的情况;

  • 不能用于浮点数、字符串和范围判断。

switch是“对号入座”式的分支,if是“条件判断”式的分支。值离散、分支多时用switch,条件复杂、范围判断时用if。

循环语句

1.for循环

c for (初始化表达式; 条件表达式; 更新表达式) { 循环体; }

执行流程:

  1. 执行初始化表达式,只执行一次;

  2. 判断条件表达式,为真则执行循环体,为假则结束循环;

  3. 执行循环体;

  4. 执行更新表达式;

  5. 回到第 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. 判断条件表达式;

  2. 为真,执行循环体;

  3. 回到第 1 步,继续判断;

  4. 为假,结束循环。

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 和 continue

break:如果用于循环则终止整个循环;如果用于switch则是用于终止switch;break不能直接用于if,除非if属于循环内部一个语句。

例:

c for(i=0;i<3;++i) { if(3>2) break; printf("嘿嘿!\n");//永远不会输出 }

break虽然是if内部语句,但是终止的是外面的for循环。在多层嵌套中,break只能终止最里面包裹它的那个循环。在多层 switch中终止距离最近的switch。

continue:用于跳过本次循环余下的语句,转去判断是否需要执行下次循环。

例:

c while(1) { A; B; continue; C; D; }

C和D会被跳过不执行。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询