目录
1. 算数操作符:+、-、*、/、%
在写代码的时候,一一定会设计到计算。
C语言中为了方便运算,提供了一系列的操作符,其中有一组操作符叫做:算数运算符,分别是:+ - * / %,这些操作符都是双目操作符。
注:操作符也被叫做:运算符,是不同的翻译,意思是一样的。
1.1 +和-
//加号
int main()
{
// + :操作符 33,42:操作数
//33是左操作数 42是右操作数,所以+也叫双目操作符,有两个操作数的操作符
printf("%d\n", 33 + 42);
//两个变量相加
int a = 10;
int b = 20;
printf("%d\n", a + b);
//一个变量和一个数字相加
printf("%d\n", a + 10);
return 0;
}
//减号
int main()
{
printf("%d\n", 33 - 42);
int a = 10;
int b = 20;
printf("%d\n", a - b);
printf("%d\n", a - 10);
return 0;
}
1.2 *
运算符*是用来完成乘法。
在C语言中"*"就是乘号,这个和数学中不一样。
int main()
{
printf("%d\n", 5 * 5);
int a = 10;
int b = 20;
printf("%d\n",a * b);
printf("%d\n",a * 10);
return 0;
}
5.3 /
运算符 / 是用来完成除法。
除号的两端是整数,执行的是整数除法,得到的结果也是整数。
int main()
{
printf("%d\n", 9 / 3);//3
printf("%d\n", 10 / 4);
//从数学的角度看,10 / 4是2.5,但这里算出的不是2.5,结果是2。
return 0;
}
如果希望得到浮点数的结果,两个运算数必须至少有一个浮点数,这时C语言就会进行浮点数除法。
int main()
{
printf("%d\n", 9 / 3);//3
printf("%d\n", 10 / 4);
//从数学的角度看,10 / 4是2.5,但这里算出的不是2.5,结果是2。
printf("%f\n", 10 / 4.0);//除号两端至少有一个数是浮点数,用%f打印
//这样结果就是浮点数了,也可以写成10.0/4或者10.0/4.0
return 0;
}
再看一个例子:
#include <stdio.h>
int main()
{
int score = 5;
score = (score / 20) * 100;
return 0;
}
上面的代码,你可能觉得经过计算,score会等于25,但实际上score等于0。这是因为score / 20是整数,会得到一个整数值0,所以乘以100后得到的也是0。
为了得到预想的结果,可以将除数20改成20.0,让整数除法变成浮点数除法。
#include <stdio.h>
int main()
{
int score = 5;
score = (score / 20.0) * 100;
printf("%f", score);
return 0;
}
5.4 %
运算符%表示求模(余)运算,即返回两个整数相除的余值。这个运算符只能用于整数,不能用于浮点数。
#include <stdio.h>
int main()
{
printf("%d\n", 10 / 3);//10 / 3 = 3 ... 1,得到的是整除之后的余数
//1. 就是算一个数除两一个的余数
//2. 计算一个数能不能被另一个数整除
int n = 0;
if (n % 2 == 0);//如果余数是0就表示能被整除,不是0就表示不能被整除
return 0;
}
取模运算符只能用于整型。
负数求模的规则是,结果的正负号由第一个运算符的正负号决定。
#include <stdint.h>
int main()
{
printf("%d\n", 11 % -5);//1
printf("%d\n", -11 % -5);//-1
printf("%d\n", -11 % 5);//-1
return 0;
}
上面示例中,第一个 运算符的正负号(11或-11)决定了结果的正负号。
2. 赋值操作符: = 和 复合赋值
在创建变量的时候给一个初始值叫初始化,在变量创建好后,再给一个值,这叫赋值。
#include <stdio.h>
int main()
{
int a = 0;//初始化
a = 10;//赋值 - 这个时候就要依赖赋值操作符,这里的等号就是赋值操作符
return 0;
}
2.1 连续赋值
赋值操作符也可以连续赋值。
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
int c = 0;
c = b = a + 3;//连续赋值,从右向左依次赋值
return 0;
}
C语言虽然支持这种连续赋值,但是写出的代码不容易理解,建议还是拆开来写,这样方便观察代码的执行细节。
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
int c = 0;
b = a + 3;
c = b;
return 0;
}
这样写,在调试的时候,每一次赋值的细节都是可以很方便观察的。
6.2 复合赋值符
#include <stdio.h>
int main()
{
int a = 0;
a = a + 10;//a加上10再赋给a,就是让a加上10.
a += 10;//也可以这样写,这句代码和上面的是等价的 - 复合赋值
a = a - 3;
a -= 3;
//+=,-=都可以,那么*=、/=、%=都是可以的
return 0;
}
这样的代码C语言提供了更加方便的写法。
#include <stdio.h>
int main()
{
int a = 10;
a += 3;
a -= 2;
return 0;
}
C语言中提供了复合赋值符,方便我们编写代码。
+= -=
*= /= %=
>>= <<=
&= |= ^=
3. 单目操作符: ++、--、+、-
签名吗介绍的操作符都是双目操作符,有两个操作数的。C语言中还有一些操作符只有一个操作数,被称为单目操作符。++、--、+(正)、-(负)就是单目操作符。
3.1 ++和--
++是一种自增的操作符,又分为前置++和后置++,--是一种自减的操作符,也分为前置--和后置--。
#include <stdio.h>
int main()
{
int a = 5;
//a = a + 1;//a自增1
//a++;//也可以这样写
++a;//还可以这样写
printf("%d\n", a);
// 当然 -- 也是类似
return 0;
}
3.1.1 前置++
#include <stdio.h>
int main()
{
int a = 5;
int b = ++a;//前置++
//前置++的计算方式是先++后使用
// 赋值步骤:a = a+1 , b = a
// 结果:a = 6 , b = 6
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
计算口诀:先+1,后使用
a原来是5,先+1后a变成6,再使用就是赋值给b,b得到的也就是6,所以计算完成后,a和b都是6,相当于下面的代码。
#include <stdio.h>
int main()
{
int a = 10;
a = a + 1;
int b = a;
return 0;
}
3.1.2 后置++
#include <stdio.h>
int main()
{
int a = 5;
int b = a++;//后置++
//后置++的计算方式是先使用后++
// 赋值步骤:b = a ,a = a+1
// 结果:a = 6 , b = 5
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
计算口诀: 先使用,后+1
a原来是5,先使用就是先赋值给b,b得到了5,然后再+1,然后a就变成了6,所以执行结束后a是6,b是5,相当于下面的代码。
#include <stdio.h>
int main()
{
int a = 10;
int b = a;
a = a + 1;
return 0;
}
3.1.3 前置--
计算口诀: 先-1,后使用
#include <stdio.h>
int main()
{
int a = 5;
int b = --a;//前置--
//前置--的计算方式是先--后使用
// 赋值步骤:a = a-1 , b = a
// 结果:a = 4 , b = 4
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
3.1.4 后置--
计算口诀:先使用,后-1
#include <stdio.h>
int main()
{
int a = 5;
int b = a--;//后置--
//后置--的计算方式是先使用后--
// 赋值步骤:b = a , a = a-,
// 结果:a = 4 , b = 5
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
注意:
#include <stdio.h>
int main()
{
int a = 5;
//printf("%d\n", --a);//前置--,先--,后打印 - 4;
printf("%d\n", a--);//后置--,先打印,后--
printf("%d\n", a);
return 0;
}
3.2 + 和 -
这里的+是正号,-是负号,都是单目操作符。
运算符+对正负值没有影响,是一个可以完全省略的运算符,但是写了也不会报错。
#include <stdio.h>
int main()
{
printf("%d\n", 10);
printf("%d\n", +10);//这段代码和上面的等价
int a = 10;
printf("%d\n", a);
printf("%d\n", +a);//等价
}
运算符 - 用来改变一个值的正负号,负数的前面加上 - 会得到正数,正数的前面加上 - 会得到负数。
#include <stdio.h>
int main()
{
int a = -10;
printf("%d\n", -10);
printf("%d\n", -a);//负的负10就是10,负负得正
printf("%d\n", 10);
return 0;
}
4. 强制类型转换
在操作符中还有一种特殊的操作符是强制类型转换,语法形式很简单.
(类型)
代码:
#include <stdio.h>
int main()
{
//int a = 10;//整数赋给整型没有问题
//那如果我把3.14赋值给a
int a = 3.14;
//编译器会认为3.14是double类型,赋给整型,两边类型不一样,会报警告
//double类型是8个字节,int是4个字节,会丢失数据
//那么我就想把3.14赋值给a这个整型类型的话我们就得用强转
int a = (int)3.14;
//括号里面写int就是把3.14强转为整型,强转只取整数部分,小数部分丢弃
printf("%d\n", a);
return 0;
}
俗话说,强扭的瓜不甜,我们使用强制类型转换都是万不得已的时候使用,如果不需要强制类型转换就能实现代码,这样自然更好。
5. scanf和printf介绍
5.1 printf
5.1.1 基本用法
printf()的作用是将参数文本输出到屏幕,print是打印的意思,它名字里面的f表示format(格式化),表示可以定制输出文本的格式。
#include <stdio.h>
int main()
{
printf("hello world");//printf是库函数,需要包含头文件stdio.h
return 0;
}
上面代码会在屏幕上输出一行文字"hello world"。
printf()不会在行尾自动添加换行符,允许结束后,光标就停留在输出结束的地方,不会自动换行。
为了让光标移动到下一行的开头,可以在输出文本的结尾,添加一个换行符\n。
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
如果文本内部有换行,也是通过插入换行符来实现。
#include <stdio.h>
int main()
{
printf("hello\nworld");
return 0;
}
printf()是在标准库的头文件stdio.h定义的。使用这个函数之前,必须在源码文件头部引入这个头文件。
5.1.2 占位符
printf()可以在输出文本中指定占位符。
所谓"占位符",就是这个位置可以用其他值来带入。
#include <stdio.h>
int main()
{
printf("%d\n", 100);//这个地方的%d就是占位符
//%d这个会输出一个整数,那么这个整数就是后面的值100,所以打印的话会打印100
printf("%dabc\n", 100);
//先打印%d位置的100,后打印abc
return 0;
}
占位符第一个字符一律为百分号%,第二个字符表示占位符的类型,%d表示这里代入的值必须是一个整数。
常用的占位符除了%d,还有%s表示代入的是字符串。
#include <stdio.h>
int main()
{
printf("%s will come tonight\n", "lisi");
//%s是占位符号,这里要代入字符串,所以
//结果就是lisi will come tonight
printf("%s will come tonight\n", "wangwu");
return 0;
}
输出文本里面可以有多个占位符。
#include <stdio.h>
int main()
{
printf("%s says it is %d o'clock\n", "lisi", 12);//lisi说现在12点
//这里的%s就被lisi替换,%d就被10替换,前面占位符的顺序是
//字符串,整型,那么后面的顺序也就是字符串,整型
printf("%s says it is %d o'clock\n", "cuihua", 18);//翠花说现在18点
return 0;
}
注意:
j既然写了占位符,那么参数就要一一匹配,要不然会有很大的问题。
printf()参数与占位符是一一对应的关系,如果有n个占位符,printf()的参数应该有n+1个,如果参数个数少于对应的占位符,printf()可能会输出内存中的任意值。
5.1.3 占位符例举
printf()的占位符有许多种类,与C语言的数据类型相对应。下面按照字母顺序,列出常用的占位符,方便查找。
#include <stdio.h>
int main()
{
//%c - 字符
printf("this is %c\n", 'A');
//%hd - 十六进制的short int类型
printf("%hd\n", 100);
//%ho - 八进制的short int类型
printf("%ho\n", 100);
//%x - 十六进制打印
printf("%x\n", 15);
return 0;
}
5.1.4 输出格式
printf()可以定制占位符的输出格式。
5.1.4.1 限定宽度
printf()允许限定占位符的最小宽度。
#include <stdio.h>
int main()
{
printf("%d\n", 123);
printf("%5d\n", 123);//最少输出5位,输出为" 123"
printf("%5d\n", 123456);//最小宽度5,如果长度超过5位,就按实际情况打印,这里6位
return 0;
}
%5d表示这个占位符的宽度至少为5位。如果不满5位,对应的值的前面会添加空格。输出的值默认是右对齐,即输出内容前面会有空格,如果希望改成左对齐,在输出内容后面添加空格,可以在占位符的%的后面插入一个-号。
#include <stdio.h>
int main()
{
printf("%dxxx\n", 123);
printf("%-5dxxx\n", 123);//后面的xxx是为了更直观的看见补的空格
return 0;
}
对于小数,这个限定符会限制所有数字的最小显示宽度。
#include <stdio.h>
int main()
{
printf("%lf\n", 123.45);//直接写123.45默认是double类型的值,所以用%lf
//%f和%1f在打印的时候, 小数点后默认是打印6位
printf("%12lf\n", 123.45);//最少12位,这个12位包含小数点和小数点后面的位数
return 0;
}
%12lf表示输出的浮点数最少要占据12位,由于小数的默认显示u精度是小数点后6位,所以123.45输出结果的头部会添加两个空格。
5.1.4.2 总是显示正负号
默认情况下,printf()不对正数显示+号,只对负数显示-号,如果想让正数也输出+号,可以在占位符的%后面加一个+。
#include <stdio.h>
int main()
{
printf("%+d\n", 123);
printf("%+d\n", -123);
return 0;
}
5.1.4.3 限定小数位数
输出小数时,有的时候希望限定小数的位数。如果希望小数点后面只保留两位,占位符可以写成%.2f。
#include <stdio.h>
int main()
{
printf("%lf\n", 123.45);//默认小数点后面打印6位
printf("%.2lf\n", 123.45);//小数点后面打印2位
printf("%.3lf\n", 123.45);//小数点后面打印3位
printf("%.1lf\n", 123.45);//小数点后面打印1位,四舍五入了
printf("%.8lf\n", 123.45);//小数点后面打印8位
return 0;
}
这种写法可以与限定宽度占位符结合使用,既限制小数点位数,也限制宽度。
#include <stdio.h>
int main()
{
printf("%12.1f\n", 123.45);//.1表示小数点后只有一位,前面的12表示打印12位
printf("%3.1f\n", 123.45);//这里写3是不行的,还是正常打印几位就几位
return 0;
}
最小宽度和小数位数这两个限定值都可以用*代替,通过printf()的参数传入。
#include <stdio.h>
int main()
{
printf("%12.1f\n", 123.45);
printf("%*.*f\n",12, 1, 123.45);//12对应的就是第一个*,1对应的就是第二个*
return 0;
}
5.1.4.4 输出部分字符串
%s占位符用来输出字符串,默认是全部输出。如果只想输出开头的部分,可以用%.[m]s指定输出的长度,其中[m]代表一个数字,表示所要输出的长度。
5.2 scanf
当我们有了变量,我们需要给变量输入值就可以使用scanf函数,如果需要将变量的值输出在屏幕上的时候可以使用printf函数。
#include <stdio.h>
int main()
{
int score = 0;
printf("请输入成绩:");
scanf("%d", &score);//输入操作
//scanf函数中占位符的后面的参数需要的是地址
//& 取地址操作符,&score - 取出score的地址
printf("成绩是:%d\n", score);
return 0;
}
画图演示:
注:标准输入一般指的就是键盘,标准输出一般指的就是屏幕。
当我们写好代码运行的时候发现这里报错了,这种错误只在VS上出现,这里报错的原因就是scanf这个函数不安全,vs提示scanf函数不安全,考虑使用scanf_s来替换,但是如果使用scanf_s的话就需要知道这个函数的使用方式和scanf不完全相同,所以不建议用scanf_s,后面又说了,如果想要这个错误信息失效的话就请使用_CRT_SECURE_NO_WARNINGS,那这个应该怎么使用呢?
1.#define _CRT_SECURE_NO_WARNINGS 1 ,将这句代码放在使用scanf函数的.c文件的第一行。
2. 每次写代码都要在源文件前面加这段代码,特别的麻烦,那么我们可以适当的设置一下。
在VS上这种.c/.cpp的文件,新建的时候其实是拷贝了newc++file.cpp的内容
如果在newc++file.cpp的文件中增加:#define _CRT_SECURE_NO_WARNINGS 1,以后新建.c/.cpp文件的时候就自动有这句话。
在自己VS的安装目录下找到该文件,然后使用记事本打开,将上面的话拷贝到里面,然后点击保存。
如果保存的时候权限不够可以先拷贝到桌面上,桌面的权限还是挺大的,然后修改好了直接拷贝到原位置,替换掉原来的文件。 (记得往桌面弄的时候一定是赋值,不是拖拽,要不然安装目录中就没有newc++file.cpp文件)。
当我们安装路径下面没有newc++file.cpp文件的时候都创建不了.c/.cpp文件。
注意:vs2022创建的时候必须是显示所有模板,京凑视图创建的也是没有的。
5.2.1 基本用法
scanf()函数用于读取用户的键盘输入。
程序运行到这个语句时,会停下来,等待用户从键盘输入。
用户输入数据,按下回车键后,scanf()就会处理用户的输入,将取存入变量。
它的原型定义在头文件stdio.h中。
scanf()的语法跟printf()类似。
scanf("%d",&i);
它的第一个参数是一个格式字符串,里面会放置占位符(与printf()的占位符基本一致),告诉编译器如何解读用户的输入,需要提取的数据是什么类型。
这是因为C语言的数据都是有类型的,scanf()必须提前知道用户输入的数据类型,才能处理数据。
它的其余参数就是存放用户输入的变量,格式字符串里面有多少个占位符就有多少个变量。
上面的代码中,scanf的第一个参数是%d,表示用户输入的应该是一个整数。%d就是一个占位符,%是占位符的标志,d表示整数。第二个参数&i表示,将用户从键盘输入的整数存入变量i。
下面是一次将键盘输入读入多个变量的例子。
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
float f1 = 0.0;
float f2 = 0.0;
scanf("%d %d %f %f", &a, &b, &f1, &f2);//占位符后面的参数必须是地址
printf("%d %d %f %f", a, b, f1, f2);
return 0;
}
上面示例中,格式字符串%d%d%f%f,表示用户输入的前两个是整数,后两个是浮点数,比如1 2 3.0 4.2,这四个依次放入a,b,f1,f2四个变量中。
scanf()处理数值占位符时,会自动过滤空白自发粉,包括空格,制表符,换行符等。
所以,用户输入的数据之间,有一个或多个空格不影响scanf()解读数据。另外,用户使用火车键,将输入分成几行,也不影响解读。
上面示例中,用户分成四行输入,得到的结果与一行输入是完全一样的。每次按下回车键以后,scanf()就会开始解读,如果第一行匹配第一个占位符,那么下次按下回车键时,就会从第二个占位符开始解读。
Scanf()处理用户输入的原理是,用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。
解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止。 解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止。
#include <stdio.h>
int main()
{
int x;
float y;
//用户输入 " -13.45e12# 0"
scanf("%d", &x);
printf("%d\n", x);
scanf("%f", &y);
printf("%f\n", y);
return 0;
}
上面示例中,scanf()读取用户输入时,%d占位符会忽略起首的空格,从-处开始获取数据,读取到-13停下来,因为后面的,不属于整数的有效字符。这就是说占位符%d会读到-13。
第二次调用scanf()时,就会从上一次停止解读的地方,继续往下读取。这一次读取的首字符是,由于对应的占位符是%f ,会读取到.45e12,这是采用科学计数法的浮点数格式。后面的#不属于浮点数的有效字符,所以会停在这里。
由于scanf()可以连续处理多个占位符,所以上面的例子也可以写成下面这样。
#include <stdio.h>
int main()
{
int x;
float y;
//用户输入 " -13.45e12# 0"
scanf("%d %f", &x,&y);
printf("%d %f\n", x,y);
return 0;
}
注意:一定要按照代码给定的格式输入输出的,如果不按照格式来,会导致问题的。
5.2.2 scanf的返回值
scanf()是有返回值的,只不过我们没有接收它的返回值而已,编译器会报警告,我们不用scanf的返回值的话可以不用管。
scanf()的返回值是一个整数,表示成功读取到变量的个数。
如果没有读取任何项,或者匹配失败,则返回0。
如果在成功读取任何数据之前,发生了读取错误或者遇到读取到文件结尾,则返回常量EOF(-1)。
EOF - end of file 文件结束标志
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
int c = 0;
int d = 0;
int ret = scanf("%d %d %d %d", &a, &b, &c, &d);
int ret = scanf("a = %d b = %d c = %d d = %d\n", a, b, c, d);
printf("ret = %d\n", ret);
return 0;
}
完全读取的情况:
非完全读取的情况:
读到第三个值的时候格式不匹配了,所以只读到了三个。
读到两个值,连续按三次Ctrl+z也会停下来,所以ret是2。 (正常的情况下应该按一次就可以了,VS需要按三次)
一个都没读到,返回的就是0。
那么我上来就连续按三次Ctrl+z,返回的就是-1,也就是EOF,所以我们通常会拿scanf的返回值判断这次是读取成功了还是失败了。 scanf的返回值如果是EOF,就说明读取失败了,反之则读取成功了。
5.2.3 占位符
scanf()常用的占位符如下,与printf()的占位符基本一致。
- %c:字符。
- %d:整数。
- %f:float类型浮点数。
- %lf:double类型浮点数。
- %Lf:long double类型浮点数。
- %s:字符串。
- %[]:在方括号中指定一组匹配的字符(比如%[0-9]),遇到不在集合之中的字符,匹配将会停止。
上面所有占位符中,除了%c以外,都会自动忽略起首的空白字符。%c不忽略空白字符,总是返回当前第一个字符,无论该字符是否为空格。
如果想要强制跳过字符前的空白字符,可以写成scanf(" %c",&ch),即%c前加上一个空格,表示跳过零个或多个空白字符。
下面要特别说一下占位符%s,它其实不能简单的等同于字符串。它的规则是,从当前第一个非空白字符开始读起,直到遇到空白字符(空格,换行符,制表符等)为止。
因为%s不会包含空白字符,所以无法用来读取多个单词,除非多个%s一起使用。这也意味着scanf()不适合读取可能包含空格的字符串,比如书名或歌曲名。另外,scanf()遇到%s占位符,会在字符串变量末尾存储一个空字符\0。
scanf()将字符串读入字符数组时,不会检测字符串是否超过了数组长度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防止这种情况,使用%s占位符时,应该指定读入字符串的最长长度,即写成%[m]s,其中[m]是一个整数,表示读取字符串的最大长度,后面的字符将被丢弃。
写成%4s的时候最多只读4个,后面的字符会被丢弃,这样就不会有数组溢出的风险了。
5.2.4 赋值忽略符
有的时候,用户的输入可能不符合预定的格式。
#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\n", year, month, day);
return 0;
}
上面的示例中,如果用户输入2024-08-22,就会正确解读出年、月、日。问题是用户可能输入其他格式,比如2024/08/22,这种情况下,scanf()解析数据就会失败。
为了避免这种情况,scanf()提供了一个赋值忽略符(assignment suppression character) *。只要把*加载任何占位符的百分号后面,该占位符就不会返回值,解析后被丢弃。
#include <stdio.h>
int main()
{
int year = 0;
int month = 0;
int day = 0;
scanf("%d%*c%d%*c%d", &year, &month, &day);
//-就是字符,加上%c,但是想要忽略这个字符的话还得加上*
printf("%d %d %d\n", year, month, day);
return 0;
}
上面的示例中,%*c就是在占位符的百分号后面加入了赋值忽略符*,这个表示占位符没有对应的变量,解读后不必返回。