
上一章我们对魔法元素(数据类型)以及存储操控魔法的容器、符文(变量)有了最基础的认知,接下来我们要驾驭最基础的魔法仪轨(操作符)和更为深邃与危险的仪式。
在进行代码组合时,往往会涉及到元素构建(计算)。C语言中为了方便构建,提供一系列魔法仪轨(操作符),其中一组魔法仪轨叫:算术操作符,是魔法世界最基础、最核心的元素炼金术。分别是:+、-、*、/、%,这些操作符都是双目操作符。 (注:操作符也被称作运算符,是不同的翻译,意思是一样的。)
+(加):是魔法中的融合咒。将两种物质或能量(如水和土)融合,创造出新的物质(泥浆)。a + b就是将a和b的“魔法能量”融合在一起。
-(减):是魔法中的分离或湮灭咒。从一个物质中抽离出另一种物质,或直接削弱其能量。a - b就是从a的“能量”消去b部分。
+和-都是有两个操作数的,位于操作符两端的是它们的操作符,因此该操作符也叫双目操作符。代码如下:
#include<stdio.h>
int main()
{
int a = 1314;
int b = 520;
int c = a + b;
int d = a - b;
printf("%d\n",c);
printf("%d\n",d);
return 0;
} 注意:数学中的值计算逻辑是由左向右,像6+6=12是由两个6加起来得到值为12。而代码中像c=a+b、d=a-b,是将a和b加或减后,将结果的赋予c或d,计算逻辑是由右向左。
*(乘):是魔法中的增值或强化咒。不是简单的叠加,而是让某种物质或能量成倍的增长。a * b就像把a的“能量”复制并放大了b倍。同样 * 也是双目操作符。代码如下:
#include<stdio.h>
int main()
{
int a = 9;
printf("%d\n",a * a);
return 0;
} /(除):是魔法中的均分或精炼咒。将一个整体均分等地分割成若干份,或者从中提炼出精华。a / b就是将a的“能量”平均分割成b份,并取其中一份。
除号有一个特点:两端如果是整数,执行的是整数除法,得到的结果也是整数代码如下:
#include<stdio.h>
int main()
{
float x = 6 / 4;
int y = 6 / 4;
printf("%f\n", x);
printf("%d\n", y);
return 0;
} 
在上面的示例中,尽管变量x的类型是float(浮点数),但6 / 4得到的结果是1.0,而不是1.5。原因就在于C语言里面的整数除法是整数,只会返回整数部分,丢弃小数部分。
如果希望得到浮点数的结果,两个运算数必须至少有一个浮点数,这时C语言就会进行浮点数除法。代码如下:
#include<stdio.h>
int main()
{
float x = 6.0 / 4;
printf("%f\n", x);
return 0;
} 
可以看到,6.0 / 4表示进行浮点数除法,得到的结果就是1.5。
再看一个例子:
#include<stdio.h>
int main()
{
int num = 4;
num = (num / 20) * 100;
printf("%d\n",num);
return 0;
} 
上面的代码,你可能觉得经过运算,num会等于20,但实际上num等于0.这是因为num / 20是整数,会得到一个整数值0,所以乘以100后得到的也是0。
为了得到预想的结果,可以将除数20改为20.0,让整数变为浮点数除法。
#include<stdio.h>
int main()
{
int num = 4;
num = (num / 20.0) * 100;
printf("%d\n",num);
return 0;
}
%(取模):是魔法中的萃取余烬,在一次完整的均分后,总会留下一些无法再分的“魔法残渣”或“余烬”,这个操作符就是专门收集这些残渣。a % b就是得到均分a份后剩下的那点独特能量,即两个整数相除的余值。代码如下:
#include<stdio.h>
int main()
{
int x = 6 % 4;
printf("%d\n", x);
return 0;
} 
6除以4于2,因此打印出2。
负数求模的规则,结果的正负号由第一个运算符的正负号决定(千万别搞错了)。代码如下:
#include<stdio.h>
int main()
{
printf("%d\n",14 % 6);
printf("%d\n",-14 % 6);
printf("%d\n",14 % -6);
printf("%d\n",-14 % -6);
return 0;
} 
可以看到上面示例中,第一个运算符的正负号(14或-14)决定结果的正负号。
在变量创建的时候给一个初始值叫初始化,在变量创建好后,再给⼀个值,这叫赋值。赋值操作符不是创造能量,而是支配和约束能量,如同与魔法精灵签订契约或给物品附魔。
=(赋值):是魔法中的主契约封印,这是最强大的束缚咒语。a = b意味着你强制命令一个叫a的魔法容器(变量),其内部必须容纳b所代表的能量或实体,之前的任何内容都会被覆盖和清除。一个随时可以给变量赋值的操作符
#include<stdio.h>
int main()
{
int a = 100;//初始化
a = 520;//赋值,这里使用的就是赋值操作符
printf("%d\n", a);
return 0;
} 
可以看到a的值最终为520;
赋值操作符也可以连续赋值,如:
#include<stdio.h>
int main()
{
int a = 5;
int b = 2;
int c = 0;
c = b = a + 4;//连续赋值,从右向左依次赋值
printf("%d\n", b);
printf("%d\n", c);
return 0;
} 
C语⾔虽然⽀持这种连续赋值,但是写出的代码不容易理解,建议还是拆开来写,这样⽅便观察代码的执⾏细节。
#include<stdio.h>
int main()
{
int a = 5;
int b = 2;
int c = 0;
b = a + 4;
c = b;
printf("%d\n", b);
printf("%d\n", c);
return 0;
}
这样写,在调试的是,每⼀次赋值的细节都是可以很方便的观察。
+=、-=、*=等(复合赋值):是魔法中的契约修订或附魔强化。这些是更高效、更强大的契约咒语。它们同时完成“计算”和“封印”两个动作。 在写代码时,我们经常可能对一个数进行自增、自减等操作,如下代码:
#include<stdio.h>
int main()
{
int a = 10;
a = a + 2;
a = a - 4;
a = a * 2;
a = a / 4;
printf("%d\n", a);
return 0;
}
这样的代码C语言提供了更加方便的写法:
#include<stdio.h>
int main()
{
int a = 10;
a += 2;
a -= 4;
a *= 2;
a /= 4;
printf("%d\n", a);
return 0;
}
C语言中提供了复合赋值符,方便我们编写代码,这些赋值符有:
+= -=
*= /= %=
//下面的操作符后期讲解
>>= <<=
&= |= ^=前面介绍的操作符都是双目操作符,要有2个操作数。C语言还有一些操作符只有一个操作数,被称为单目操作符。++、–、+(正)、-(负)就是单目操作符。
++是一种自增的操作符,又分为前置++和后置++,–是一种自减的操作符,又分为前置–和后置–。
++/–(自增/自减):是魔法中的能量灌注/抽取术。这是瞬间生效的强化或削弱魔法。a++或++a是向a注入一单位纯粹的魔法能量,使其强大一分。a–或–a则是瞬间抽走一单位能量。
#include<stdio.h>
int main()
{
int a = 10;
int b = ++a;
printf("a = %d b = %d\n", a, b);
return 0;
} 
计算口诀:先+1,后使用; a原来是10,先+1,后a变成了11,再使⽤就是赋值给b,b得到的也是11,所以计算后,a和b都是11,相当于这样的代码:
#include<stdio.h>
int main()
{
int a = 10;
int b = 0;//这里随便给b赋个值
a = a + 1;
b = a;
printf("a = %d b = %d\n", a, b);
return 0;
}
#include<stdio.h>
int main()
{
int a = 10;
int b = a++;
printf("a = %d b = %d\n", a, b);
return 0;
}
计算口诀:先使用,后+1; a原来是10,先使用,就是先赋值给b,b得到了10,然后再+1,然后a变成了11,所以直接结束后a是11,b是10,相当于这样的代码:
#include<stdio.h>
int main()
{
int a = 10;
int b = a;
a = a + 1;
printf("a = %d b = %d\n", a, b);
return 0;
}
如果你明白了前置++,那前置–是同理的,只是把加1,换成减1。 计算口诀:先-1,后使用
#include<stdio.h>
int main()
{
int a = 10;
int b = --a;
printf("a = %d b = %d\n", a, b);
return 0;
}
同理后置–类似于后置++,只是把加⼀换成了减⼀。 计算口诀:先使用,后-1
#include<stdio.h>
int main()
{
int a = 10;
int b = a--;
printf("a = %d b = %d\n", a, b);
return 0;
}
这里的+是正号,-是负号,都是单目操作符。 运算符+对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。
int a = +10; 等价于 int a = 10;运算符-用来改变⼀个值的正负号,负数的前面加上就会得到正数,正数的前面加上- 会得到负数。
#include<stdio.h>
int main()
{
int a = 10;
int b = -a;
int c = -10;
printf("b = %d c = %d\n", b, c);//这里的b和c都是-10
return 0;
}
#include<stdio.h>
int main()
{
int a = -10;
int b = -a;
printf("b = %d\n", b);
return 0;
}
这是一种强大但危险的高阶魔法,他试图改变一个存在的“本质形态”(数据类型)。 就像一个水元素(float),你对它释放(int) 变形术,想把它变成一个土元素(int)。而这个魔法不会创造一个全新的土元素,它只是在**水元素外面强行套上一个土元素的“外壳”或“模板”。世界(编译器)会暂时把它当作土元素来看待(进行整数运算)。代码如下:
int a = 3.14;
//a是int类型,3.14是double类型,两边的类型不一致,编译器会警告
//但会打印,且打印为3为了消除这个警告,我们可以使用强制类型转换:
int a = (int)3.14;
//意思是将3.14强制类型转换为int类型,这种强制类型转换只取整数部分
//同时3.14编译器(VS2022)一般默认为double类型,若想为float类型,在后面加f
int a = 3.14f;但这种魔法存在很大的风险,像数据截断丢失,甚至数据溢出,引发未定义行为。故我们使用强制类型转换都是万不得已是使用,如果不需要就能实现代码,这样自然更好。
printf是将内部能量(数据)以可控的、格式化的、人类可读的形式投影到外部世界的魔法。其中名字里的f 代表format(格式化),表示可以定制输出文本格式。
#include<stdio.h>
int main()
{
printf("Hello World")
return 0;
} 上输出y一行文字“HelloWorld”。 printf() 不会在行尾自动添加换行符,运行结束后,光标就停留在输出结束的地方,不会自动换行。为了让光标移到下一行的开头,可以在输出文本的结尾,添加⼀个换行符\n。
#include<stdio.h>
int main()
{
printf("Hello World\n")
return 0;
}如果文本内部有换行,也是通过插⼊换行符来实现,如下方代码:
#include<stdio.h>
int main()
{
printf("Hello\n World\n");
printf("Hello\n");
printf("World\n");
return 0;
}
printf()是标准库的头文件stdio.h定义的。使用这个函数之前,必须在源码文件头部引入这个头文件。
printf()可以在输出文本中指定占位符。 而占位符可以当作魔法仪式中的符文凹槽、能量透镜或契约咒印。它们是仪式模板中预留的关键接口,决定了外来能量将以何种形态被吸纳,或者内部能量将以何种形式被显现。
#include<stdio.h>
int main()
{
printf("There are %d apples\n",8);
return 0;
} 上面示例中,There are %d apples\n是输出文本,里面的%d 就是占位符,表示这个位置要用其他值来替换。占位符的第⼀个字符⼀律为百分号%第⼆个字符表示占位符的类型,%d表示这里代入的值必须是⼀个整数。 printf() 的第二个参数就是替换占位符的值,上面的例子是整数3替换%d。执行后结果是“There are 3 apples”。
常用的占位符除了%d,还有%s表示代入的是字符串。
#include<stdio.h>
int main()
{
printf("%s will come China","zhangsan");
return 0;
} 上面示例中,%s表示代入的是一个字符串,所以printf()的第二个参数就必须是字符串。执行后结果为“zhangsan will come China”。
输出文本里面可以使用多个占位符。
#include<stdio.h>
int main()
{
printf("%s says it is %d o'clock", "lisi", 12);
return 0;
} 上面示例中,输出文本“%s says it is %d o’clock”有两个占位符,第一个是字符串占位符%s,第二个是整数占位符%d,分别对应printf()的第二个参数( lisi )和第三个参数(12)。执行后结果“lisi says it is 12 o’clock”。
printf()参数与占位符是一一对应关系,若有n个占位符,printf()的参数就应该有n+1个。如果参数少于对应的占位符,printf()可能会输出内存中的任一值。

printf()的占位符有许多种类,与C语言的数据类型相对应。下面列出常用的占位符,方便查找,具体含义后面会介绍。
printf()可以定制占位符的输出格式。
printf()允许限定占位符的最小宽度
#include<stdio.h>
int main()
{
printf("%6d\n",123);//输出" 123"
return 0;
} 上面示例中,%6d表示这个占位符的宽度至少为6位。如果不满6位,对应的值的前面会添加空格。输出的值默认是右对齐,即输出内容前面会有空格;如果希望改成左对齐,在输出内容前面添加空格,可以在占位符的%的后面插入一 -(负号)。
#include<stdio.h>
int main()
{
printf("%-6d\n",123);//输出"123 "
return 0;
} 上面示例中,输出内容123有三个空格。
对于小数,这个限制符会限制所有数字的最小显示宽度。
//输出" 123.450000"(默认小数点后会有6位数)
#include<stdio.h>
int main()
{
printf("%12f\n", 123.45);
return 0;
} 上面示例中,%12f表示输出的浮点数最少要占据12位。由于小数的默认显示精度是小数点后6位,所以123.45输出结果的头部会添加2个空格。
默认情况下,printf()不对正数显示+号,只对负数显示-号。如果想让正数也输出+号,可以在占位符的%后面加个+。
#include<stdio.h>
int main()
{
printf("%+d\n", 12);
printf("%+d\n", -12);
return 0;
} 上面示例,%+d可以确保输出的数值,总是带有正负号。
输出小数时有希望限定小数的位数。举例来说,希望小数点后面只保留两位,占位符可以写成%.2f。
//输出5.20
#include<stdio.h>
int main()
{
printf("5.20");
return 0;
} 上面示例中,如果希望小数点后面输出3位(5.200),占位符就要写成%。3f。
这种写法可以与限定宽度占位符,结合使用。
//输出为" 5.20"(前面有三个空格)
#include<stdio.h>
int main()
{
printf("%6.2f\n", 5.20);
return 0;
} 上面示例中,%6.2f表示输出字符的最小宽度为6,小数位为2.所以,输出字符串的头部有两个格。
最小宽度和小位数这两个限定值,都可以用 * 代替,通过printf()的参数传入。
#include<stdio.h>
int main()
{
printf("%*,*f\n", 6, 2 ,5.20);
return 0;
} 上面示例中,%*,*f的两个星号通过printf()的两个参数6和2传入。
%s占位符用来输出字符串,默认是全部输出。如果只想输出开头部分,可以用%.[n]s指定输出长度。其中[n]代表一个数字,表示所要输出的长度。
//输出 hello
#include<stdio.h>
int main()
{
printf("%.5s\n","hello world");
return 0;
} 上面示例中,占位符%。5f表示只输出字符串前五个字符,即“world”。
当我们有了变量,我们需要给变量输入值就可以使用scanf函数,需要将变量的值输出在屏幕上用printf。scanf就像一个能量识别器和过滤器,确保只有符合要求的能量才能引入你的魔法体系中,从而避免导致因能量不纯而导致仪式失败或反噬(输入错误)。
#include<stdio.h>
int main()
{
int n = 0;
printf("你的年龄");
scanf("%d", &n);
printf("年龄是:%d\n", n);
return 0;
} 
画图演示:


标准输入一般指的是键盘,标准输出一板砖的是屏幕
scanf() 函数用于读取用户的键盘输⼊。 程序运行到这个语句时,会停下来,等待用户从键盘输⼊。 用户输⼊数据、按下回车键后,scanf() 就会处理用户的输⼊,将其存⼊变量。 它的原型定义在头⽂件stdio.h 。 scanf() 的语法跟printf() 类似。
scanf("%d",&i);因为C语言的数据是有类型的,scanf()必须提前知道用户输入的数据类型,才能处理数据。
它其余参数就是存放用户输入的变量,字符串里有多少占位符,就有多少变量。并且scanf函数不能使用\n
注意:变量前面必须加上&运算符(指针变量除外)
下面是读入多个变量并输出的例子:
scanf("%d%d%f%f", &x, &y, &z, &i);
printf("%d %d %f %f", x, y, z, i);scanf()处理占位符会自动过滤空白字符,有空格、制表符、换行符等。 scanf()处理的原理是,用户先输入缓存,按下回车键后,按照占位符对缓存进行解读。 解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或遇到第一个不符合条件的字符为止。
scanf()返回值是一个整数,表示成功读取的变量个数。 如果没有读取任何像,或匹配失败,返回0。 如果在成功读取任何数据之前,发生读取错误或遇到读取文件结尾,则返回常量EOF(-1)。(EOF - end of file文件结束标志)
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
float f = 0;
int x = scanf("%d%d%f", &a, &b, &f);
printf("a=%d b=%d f=%f\n", a, b, f);
printf("x=%d\n",x);
return 0;
} 输入输出测试:

因为x是接收返回值,x成功读取a,b,f三个数,则x=3。
如果输入2个数,按ctrl+z,提前结束输入:

不同的配置按ctrl+z次数不同。按完后,可以看到x=2,表示正确读取2个值。
如果一个字不输,直接按几次ctrl+z,输出的x=-1,也就是EOF。
scanf()与printf()占位符基本一样。
在所有占位符中,除%c外,都会自动忽略起首的空白字符。%c不忽略空白字符,总是返回当前第一个字符,无论是否为空格。若强制跳过,可写scanf(“%c”, &ch)。
占位符%s不会包括空白字符,故scanf()无法读取包含空格的字符串,且遇到%s占位,会在字符串变量末尾储存一个空字符\0。
有时,魔法师的魔法可能不符合预定的构架。
#include<stdio.h>
int main()
{
int year = 0;
int month = 0;
int day = 0;
scanf("%d-%d-%d", &year, &month, &day);
printf("%d %d %d", year, month, day);
return 0;
} 上面示例中,若用户输入2025-9-2,会正确解读。但要输入其他格式如2025/9/2,就无法输出。为了避免这种情况,scanf()提供了赋值忽略符,类似于隐身咒。只要把*加在任何占位符的%后,该占位符就不会返回值,解析后将会丢弃。
#include<stdio.h>
int main()
{
int year = 0;
int month = 0;
int day = 0;
scanf("%d%*c%d%*c%d", &year, &month, &day);
printf("%d %d %d", year, month, day);
return 0;
} 上面示例中,%*c就是在占位符的百分号后面,加入赋值忽略符 *,表示这个占位符没有对应的变量,解读后不用返回。
(可能会有一些小毛病,还望各位指正)