如何打开项目
如何打开已经存在的解决方案?
找到要打开的解决方案目录,进去之后双击后缀为.sln的文件即可打开该解决方案。
或者从最近打开项目中打开:
Online Judge使用
OJ简介
在线判题系统(Online Judge,缩写OJ)是一种在编程竞赛中用来测试参赛程序的在线系统,也可以用于平时练习。
评测状态是指代码在提交后进行评测的阶段或结果,会向用户进行反馈。
使用OJ的注意事项
所有的OJ都是完美匹配输入和输出的。
OJ的output会接收到所有的标准输出printf。例如下图所示例子:
在以后机试中或者公司的面试中使用的OJ也是同样的,要严格遵守规范。(有的甚至后面多一个空格都不让Accepted,有的好一点后面有空格会自动给你去掉)。
以下才会被OJ通过,所以在OJ上答题时不能有多余的输入和输出。
数据类型
C语言中数据类型有:
- 基本类型
- 整型int
- 字符型char
- 浮点型:单精度浮点型float、双精度浮点型double
- 构造类型
- 数组类型[]
- 结构类型struct
- 联合类型union
- 枚举类型enum
- 指针类型 *
- 空类型(无值类) void
C语言中的关键字(关键字都是小写字母)如下表。注:不能使用关键字作为变量名。
变量与常量
常量
int main() {
int a = 3;//a是一个变量,3是常量,这句话计算机是将常量3 复制 给了a
a = 5;//变量a是有自己的内存地址的,而常量则没有
}
常量没有自己的内存地址,那么常量在哪呢?
程序加载到内存里面有一个编译后的.exe,.exe就是一个代码段,程序一旦运行就会将.exe放到内存的代码段,而常量3是编到代码段里的,代码段里的内容是不可变的。 变量a是有自己的内存空间的,代码是一句一句向下执行,当执行到a=3时,会将3复制到a的地址。如下图:
变量
变量的命名规定如下:C语言规定标识符只能由字母、数字、和下划线三种字符组成,并且第一个字符必须为字母或下划线。
如何查看变量地址
利用监视调试窗口查看变量变化。
int main() {
int a = 3;//a是一个变量
a = 5;
}
进入调试窗口要先打上断点,然后运行。
点下一步,int a = 3;运行结束后此时看到a内存地址里的值变成了3。
再点下一步,a = 5;执行完,a内存地址里的值又变成了5。
符号常量
用define关键字定义的就是符号常量。符号常量也是常量,不可以再被赋值。
#define PI 3 //PI就是一个符号常量。
int main() {
printf("%d\n",PI);
}
常量不可以再被修改,这里再把PI的值赋予了10,发生了报错
当VS出现如上图所示的弹窗提醒发生错误时,不管什么时候都要点“否”,然后找到错误并修改。
查看错误报告:
表达式左边称左操作数,右边称右操作数;左值是指可以修改的值,也就是说只有变量才能叫左值。
进制变换
观察内存使用的是十六进制。
如下代码段:
int main() {
int i = 123;
}
当int i = 123;这句代码还没有执行时,微软会给变量i一个初始值,十六进制下就是cccc cccc,对应的十进制-858993460。(int型占4B,C语言中int用补码表示)。
当int i = 123;这句代码执行过之后,此时i=123,变量i的内存地址里存储的便是123,对应的是十六进制7B,计算机中采用小端存储(多字节数据中,最低有效字节放在低地址,最高有效字节放在高地址,方便计算机处理数据),故内存中存储的机器数是7B 00 00 00。[补充:x86架构都是小端存储,Intel和AMD的CPU采用的都是x86架构]
在监视窗口下直接输入变量名i,仅显示 值 ,而不显示地址。
scanf函数
在同一个解决方案下新建两个项目
当在一个解决方案里面新建两个项目时,在解决方案的位置点击右键,然后按如下步骤操作:
在新建的项目里添加main.c文件。[一个项目里可以有多个代码文件,但同一个项目里所有的代码文件只能有一个入口main。]
设置启动项目
想让哪个项目运行,要把该项目设置为启动项目。
启动项目对应的项目名称是粗体,以此来分辨哪个是启动项目。
VS是为开发大型项目来准备的,所以每次都要新建一个项目,这样如果用了多个.c文件,就会自动的编译到一起,变为一个可执行文件.exe,非常方便。
scanf函数的原理
往屏幕输出是printf;从屏幕中读取数据,称为读取标准输入,用scanf。
C语言通过scanf函数读取键盘输入,键盘输入又被称为标准输入。当scanf函数读取标准输入时,如果还没有输入任何内容,那么scanf函数会被卡住(专业用语为阻塞)。
#include <stdio.h>
int main() {
int a;
scanf("%d", &a);//此时一定要在变量前加入取地址符号&
printf("a=%d\n", a);
}
当仅编入如上代码时,会发生输入报错,原因是微软认为scanf函数不安全,建议用微软自己的scanf_s函数,但**千万不能使用**scanf_s函数,首先这个scanf_s不属于C语言,其次在复试中使用scanf_s是编译不通过的,所以千万不能使用scanf_s。
解决上述报错仅需在第一行(必须是在第一行)添加如下内容 #define _CRT_SECURE_NO_WARNINGS
即可,其中_CRT_SECURE_NO_WARNINGS是上图中报错提示中出现的。[注]:该报错仅会在vs2017版本及之后才会出现,如果是更低的版本则不会出现该问题,在复试中看学校使用的是什么版本,再看用不用加;vs2012、2013、2015都不需要加。
#define _CRT_SECURE_NO_WARNINGS //解决scanf编译报错问题
#include <stdio.h>
int main() {
int a;
scanf("%d", &a);//此时一定要在变量前加入取地址符号&
printf("a=%d\n", a);
}
其中由于没有接收scanf的返回值,所以提示波浪线,定义一个返回值之后便消失了,不过这都没有影响,这里接不接返回值都可以。
想要项目只编译,不运行,只需点重新生成即可。
由于用的是 %d,所以只能输入整数。要输入两个整数就是 %d%d (中间不要有空格)。
#define _CRT_SECURE_NO_WARNINGS //解决scanf编译报错问题
#include <stdio.h>
int main() {
int a,b;
//输入两个整数
scanf("%d%d", &a,&b);//此时一定要在变量前加入取地址符号&
printf("a=%d,b=%d\n",a,b);
}
输出两个值的和:
#define _CRT_SECURE_NO_WARNINGS //解决scanf编译报错问题
#include <stdio.h>
int main() {
int a,b;
scanf("%d%d", &a,&b);//此时一定要在变量前加入取地址符号&
printf("%d\n",a+b);
}
整型
x86表示所建的程序是32位的(32位的控制台应用程序)。项目有32位和64位之分。
%d的使用
%d
就是以十进制方式输出某一个整数,要输出两个整数就要%d%d
,同理每多输出一个整数都要多加一个%d
(只能输出整数而不能输出浮点数)。
int main() {
int a = 123;
int b = 0x7b; //0x 表示十六进制
int c = 0173;//前面有个0表示八进制,八进制用的少,了解一下即可。
printf("%d\n%d\n%d\n", a,b,c);//%d 表示以十进制方式输出某一个整型数
//输出结果a,b,c均为123
}
64位CPU既可以运行32位程序也可以运行64位程序,而32位CPU只能运行32位程序。
本程序是32位程序,存储字长=32,所以一个字占32位,按字节编址,一个字占4B。可以看到每个字的地址相差4。这里地址占32位=4B,所以寻址空间是232=4GB。
浮点型
浮点数的表示形式
浮点型常量的形式有两种,如下所示,其中e代表10的幂次,幂次可正可负。
小数形式:0.123
指数形式:3e - 3 为 3 x 10-3 ,即0.003。
注意:字母e或E之前必须有数字,且e后面的指数必须为整数。
%f的使用
%f
就是以浮点数形式输出对应的数据。
float main() {
float f1 = 1.234;
printf("%f\n", f1);//输出浮点数时需要用%f。即%f就是以浮点数形式输出对应数据。
printf("%f\n", 1.234);
printf("输出浮点数 %f\n", f1);//printf里面除了%d、%f外,写什么都是字符串
float f2 = 3e-3; //不推荐使用指数形式的浮点数,不要用
printf("%f\n", f2);
float f3 = 3e3; //e表示10的幂次
printf("%f\n", f3);
}
注意在机试的时候printf里都要填英文字符串,不要用中文,防止编译错误导致乱码。甚至机试为了OJ就根本不用输入多余的字符串。
字符型数据
字符型常量
用单引号括起来的一个字符是字符型常量,且只能包含一个字符。例如’a’,‘A’,‘1’是正确的字符型常量,而’abc’,‘a’,’ ’ 是错误的字符型常量。以“\”开头的特殊字符称为转义字符,转义字符用来表示回车,退格等功能键。
各种转义字符及其作用:
\n:换行
\b:退格
\:反斜杠
%c的使用
%c表示以字符形式对后面的变量进行输出。
char c = 'a';
printf("%c\n",c);//%c表示以字符形式对后面的变量进行输出
printf("%c\n",97);//输出的也是a
system(“pause”);是VS2012版本让调试控制台停留显示必须要加的一句话,否则调试控制台将会一闪而过,不会停留。而高版本的VS则不用加这句话,同时OJ中也不要加这句话,否则OJ会判失败。
大小写转换实现
原理:查ASCII可知小写字母对应的值比大写字母对应的数值大32,所以通过这个来实现大小写转换
//大小写转换
char c = 'a';//现在是小写字母a,要变为大写字母A
c = c - 32;//查ASCII可知小写字母对应的值比大写字母对应的数值大32,所以通过这个来实现大小写转换
printf("%c\n", c);//以字符形式输出c
char d = 'A';//现在是大写字母A,要变为小写字母a
d = d + 32;//查ASCII可知小写字母对应的值比大写字母对应的数值大32,所以通过这个来实现大小写转换
printf("%c\n", d);//以字符形式输出d
字符串型常量
字符串型常量是由一对双引号括起来的字符序列。如“How do you do.” 可以通过printf(“How do you do.”)输出一个字符串。但要注意的是,'a’是字符型常量,“a”是字符串型常量,二者是不同的。单引号的是字符型,双引号的是字符串型,二者不一样。
关于字符串变量:
C语言没有专门提供一种变量类型来存字符串的。C语言是通过字符数组来存字符串的。C语言并没有像C++、Java专门发明一个String类型的去存字符串。
C语言规定,在每个字符串型常量的结尾加一个字符串结束标志,以便系统据此判断字符串是否结束。C语言规定以字符’\0’作为字符串结束标志。字符’\0’对应的ASCII码是0。
例如,字符串型常量“CHINA”在内存中的存储结果如图所示,它占用的内存单元不是5个字符,而是6个字符,即大小为6字节,最后一个字符为’\0’。然而,在输出时不输出’\0’,因为’\0’无法显示。
后面我们会把这个东西赋值给字符数组,如果不知道字符串常量字符串的大小,在后面使用的时候就会用错,实际用5个字节是放不下的。
混合运算
在C语言的不同类型的数据混合运算中, 要先转换成同一类型后进行运算。
float i = 5;
float j = i / 2;
/*
右边表达式中i为浮点型,2为整型,所以C语言会判断此为浮点型运算,便会将整型2进行自动类型提升转换为浮点数类型2.0。
浮点型运算所得结果还是浮点型,然后赋值给浮点型变量j
*/
强制类型转换
整数进行除法运算时,如果运算结果为小数,那么存储浮点数时,一定要进行强制类型转换。强制类型转换运算符( (类型) )。
下面代码中的解释一定要看,这样才能知道如果不进行强制类型转换会发生什么错误。
//混合运算
int i = 5;
float j = i / 2;
printf("%f\n", j);//输出结果为2.0
/*
解释:C语言在进行除法的时候,是如何知道表达式(等式右边的整体是一个表达式)的类型呢?这个
整体表达式的类型由谁决定呢?
表达式i / 2;其中i是整型,2也是整型,所以这是整型的除法,所以结果得到的是一个整数。再把这个
整数结果赋值给float类型,最终显示成浮点数。
*/
//想要正确的显示结果,就需要掌握强制类型转换运算符。强制类型转换运算符( (类型) )
//等式右边只有一个变量时也算是一个表达式,不管右边是什么都是表达式。
float m = (float)i;//在右边便会将i强制类型转换为float类型
float k = (float)i / 2;
/*
运算顺序,先将i强制转换为float类型,再除以2。此时i为浮点型,此时便是浮点型运算,
右边得到的结果便是2.5,再将其值赋值给k
*/
printf("j=%f,k=%f\n", j, k);//一个%f对应后面一个变量
常用的数据输入/输出函数
C语言通过scanf读取的输入叫“标准输入”。408中会出现“标准输入”等说法,需要知道指的是scanf读取的输入。printf是标准输出。
scanf读取的是标准输入;printf输出到黑窗口(控制台),输出到了标准输出。
scanf
scanf的使用方法:
//语法:
#include <stdio.h>
int scanf( const char *format, ... );
format是一个字符串。… 是可变参数,参数的数目与format中的%的数目保持一致。
format的格式:(重点掌握前三种即可)
- %d 一个十进制整数
- %f 一个浮点数
- %c 一个单一的字符
- %s 一个字符串
“%d%d%d”,&a,&b,&c 。%的个数与参数的个数要相等。且scanf的参数都要加取地址符号&。 如“%d%f%c”,&a,&b,&c ;%d、%f、%c这些都可以混合使用且不限制个数,但参数的类型要对应。
scanf的原理
当scanf函数读取标准输入时,如果还没有输入任何内容,那么scanf函数会被卡住(专业术语为阻塞)。
scanf读取标准输入缓冲区的原理:
每一个进程里都有一个标准输入缓冲区,当在黑窗口(控制台)输入内容时就会将其放入标准输入缓冲区中,但此时scanf还不会去匹配。因为scanf是行缓冲,当在输入中遇到换行符(回车键)时,才会执行真正的I/O操作(scanf从标准输入缓存区中读取数据)。
回车\n并不是什么都不存在的,\n
也会放到标准输入缓存区中。
标准输入缓冲区规定里面的所有内容均是字符,所以里面此时有三个字符1、0、\n。scanf(“%d”, &i)当写%d时就会将1、0这两个字符转成整型数存放到变量i中;这就是scanf的原理。
当按下回车键后,scanf便开始从标准输入缓冲区中读取数据,实际scanf只会将1、0读取走,\n还会留在标准输入缓冲区中。
读取完后缓冲区中还有\n这个字符。实际scanf只会将1、0读取走,\n还会留在标准输入缓冲区中。
int main() {
int i;
scanf("%d", &i);
printf("i=%d\n", i);
}
验证scanf读取走内容后缓冲区中还剩下\n:
刚开始输入10,按下回车键(换行符),scanf就会将字符1、0读走,在%d下转换为整型赋值给变量i;此时并没有把缓冲区中的\n读取走,\n还存在于标准输入缓冲区中,只有当标准输入缓冲区为空时scanf才会阻塞,但当执行到scanf(“%c”, &c)时缓冲区不为空,里面还有字符\n (\n在ASCII码中对应10),所以在scanf(“%c”, &c)处并不会发生阻塞,而会直接将\n再读取走转换成字符赋值给c。所以会看到c=回车。
int main() {
int i;
char c;
scanf("%d", &i);
printf("i=%d\n", i);
scanf("%c", &c);
printf("c=%c\n", c);
}
调出调试窗口看到c的值确实是10(\n)。
在上述例子中,我们向标准输入缓冲区中放入的字符为’10\n’,输入’\n’(回车)后,scanf函数才开始匹配,scanf函数中的%d匹配整型数10,然后放入变量i中,接着进行打印输入,这是’\n’仍然在标准输入缓冲区中,如果第二个scanf函数为scanf(“%d”,&i),那么依然会发生阻塞,因为scanf函数在读取整型数、浮点数、字符串时,会忽略’\n’(回车符)、空格符等字符,(忽略是指scanf函数执行时会首先删除这些字符,然后再阻塞。【因为只有当标准输入缓冲区为空时scanf才会阻塞,把这些字符删除掉,缓冲区就为空了,就会阻塞】)。scanf函数匹配一个字符时,会在缓冲区删除对应的字符。因为在执行scanf(“%c”,&c)语句时,不会忽略任何字符,所以scanf(“%c”,&c)读取了还在缓冲区中残留的’\n’。
根据上述原理,在执行下述代码时,printf(“f=%f\n”, f)会输出scanf读取的浮点数,而非’\n’(回车符)。根据<scanf函数在读取整型数、浮点数、字符串时,会忽略’\n’(回车符)、空格符等字符>,在执行到scanf(“%f”, &f)时会将标准输入缓存区中剩下的’\n’删除掉,然后再阻塞,等待输入。【标准输入缓冲区规定里面的所有内容均为字符,是scanf将字符读取走根据%d,%f,%c等类型转换成对应的类型】
int main() {
int i;
//char c;
float f;
scanf("%d", &i);
printf("i=%d\n", i);
//scanf("%c", &c);
//printf("c=%c\n", c);
scanf("%f", &f);
printf("f=%f\n", f);
}
第一次输入10,第二次输入98.5,得到如下结果:
根据该原理,求输入两个数的和,输入两个数时要以空格键或回车键隔开,用以区分输入了两个数。当用空格隔开时,已知scanf函数在读取整型数、浮点数、字符串时,会忽略’\n’(回车符)、空格符等字符(忽略是指scanf函数执行时会首先删除这些字符,然后再阻塞),所以并不会将空格读取到;回车键同理也不会读取到。
根据上述原理,当使用%c时则不会忽略’\n’(回车符)、空格符等字符。所以会出现如下情况:当以回车隔开时,缓冲区中是a\n
,还未等输入下一个字符scanf便会开始匹配,因为%c时并不会忽略’\n’(回车符)、空格符等字符,将字符a读走作为第一个字符后,此时本来应该阻塞等待,而在%c的情况下并不会忽略’\n’(回车符)、空格符等字符(即并不会删除这些字符,也就是缓冲区不为空,所以不会阻塞),所以会将缓冲区中留下的\n读取走作为第二个字符。以空格隔开时同理。
scanf函数在读取整型数、浮点数、字符串时,会忽略’\n’(回车符)、空格符等字符,(忽略是指scanf函数执行时会首先删除这些字符,然后再阻塞。【因为只有当标准输入缓冲区为空时scanf才会阻塞,把这些字符删除掉,缓冲区就为空了,就会阻塞】)
练习判断闰年
练习题目:判断某个年份是不是闰年,如果是闰年,请输出“yes”,否则请输出“no”。
判断一个年份是否是闰年的方法:能被400整除,或者能被4整除但不能被100整除的都是闰年,其余的年份均为平年。
int main() {
int year;
scanf("%d", &year);//scanf要加取地址符&,这点不要忘了
if (year%400==0 || (year%4==0 && year%100!=0))//这里的&&可以不加小括号,因为“&”的运算优先级高于“|”
{
printf("yes\n");
}
else
{
printf("no\n");
}
}
scanf循环读取
什么是EOF
scanf()的返回值是成功赋值的变量数量, 发生错误时返回EOF。
直接在代码里输入EOF,按着CTRL键,然后鼠标左键点击,可以在<stdio.h>中看到EOF等于常量-1。
#define EOF (-1)
说明定义了EOF是常量-1。(为什么加小括号:为了防止和其他的发生结合。高级阶段会讲什么是”结合“。)
什么情况下scanf会出错(即返回值=EOF)?
在行首连着输入ctrl z回车
三次。(必须在行首连着输入三次ctrl z 回车三次才可以)
如果没有在行首输入ctrl z 而是在字符后面输入ctrl z 回车,则便会疯狂打印不会停止。
另一种出现疯狂打印的情况:
当scanf(“%d”,&i)时,但是在调试控制台输入了一个字符,按下回车键便开始疯狂打印。
原因解释:
当输入10后按下回车键(换行符\n),scanf(“%d”,&i)只能读取标准输入缓冲区中的0-9的字符,可以将字符1、0读走并转换为整型赋值给变量i。
{因为scanf函数在读取整型数、浮点数、字符串时,会忽略’\n’(回车符)、空格符等字符,(忽略是指scanf函数执行时会首先删除这些字符,然后再阻塞。【因为只有当标准输入缓冲区为空时scanf才会阻塞,把这些字符删除掉,缓冲区就为空了,就会阻塞】)}
在scanf(“%d”,&i)下,将1、0读取走之后会将\n
删除掉,此时缓冲区为空,scanf阻塞。当此时输入字符时,如a,按下回车键,由于scanf(“%d”,&i)只能读取标准输入缓冲区中的0-9的字符,所以scanf无法读取缓冲区中的a。
scanf()的返回值是成功赋值的变量数量.
由于无法读取缓冲区中的a,故赋值失败,返回值为0。scanf(“%d”,&i)!=EOF为true,进入循环,由于缓冲区不为空,所以scanf便不会阻塞,将陷入死循环。无法读取缓冲区中的a,给变量i赋值失败,所以此时i的值还是上次赋的值,故i=10,所以便会出现疯狂打印i=10。
可以通过监视窗口看到,当输入字符时,scanf的返回值是0,即成功赋值的变量个数为0.
解决办法:在每一次scanf读取缓冲区之前将缓冲区的内容给清除掉,清除掉之后缓冲区为空,等待下一次输入。
清空缓冲区使用rewind(stdin);stdin是标准输入。
[注] rewind(stdin)仅用于Windows操作系统,Mac操作系统不能用。 且这里清空缓冲区使用rewind(stdin)只是在练习时使用,防止在练习时误输入导致死循环打印,在初始和复试中都不用写的,包括OJ也不用写,因为OJ后端检测的输入肯定是正确的输入,不会有非法输入。
//清空缓冲区,VS2012使用 fflush(stdin);新版本VS2013-VS2019使用rewind(stdin)。
// [注]fflush(stdin)在新版本中无效
//stdin是标准输入
int main() {
int i;
/*
scanf()的返回值是成功赋值的变量数量, 发生错误时返回EOF.(可查看EOF是常量-1)
所以可以通过返回值来判断是否要跳出循环。
【EOF这个宏已经在<stdio.h>中定义出来了】
*/
while (rewind(stdin),scanf("%d",&i)!=EOF)//while是实现循环,后面要有一个小括号。当为true时进入循环。
{
printf("i=%d\n", i);
}
return 0;
}
循环读取字符
//循环读取字符,将小写字母转换成大写字母
int main() {
char c;
while (scanf("%c",&c)!=EOF)//循环读取字符时不用清空缓冲区rewind(stdin),只有在读取整型数和浮点数时才使用
{
//字符用单引号'' ; 字符串用双引号""
if (c != '\n')//判断,后面有一个小括号,小括号里面是一个表达式
{
printf("%c", c-32);
}
else//else就是否则
{
printf("\n");
}
}
}
scanf的混合输入
scanf同时读取多种类型的数据,混合输入时每次在%c之前需要加入一个空格。只有混合输入里有%c时需要该操作,当混合输入里没有%c或者不是混合输入均不需要该操作。
//一个scanf读取多种类型的数据
int main() {
int i;
char c;
float f;
//混合输入时每次在%c之前需要加入一个空格。只有混合输入时需要该操作
scanf("%d %c%f", &i, &c, &f);
printf("i=%d,c=%c,f=%f\n", i, c, f);
return 0;
}
printf
printf的使用方法
printf的语法:
//printf使用方法
#include <stdio.h>
int printf( const char *format, ... );
这里format的格式同scanf中的格式一样:(重点掌握前三种即可)
- %d 一个十进制整数
- %f 一个浮点数
- %c 一个单一的字符
- %s 一个字符串
控制输出格式
printf标准输出缓冲区中都是字符串(显示在黑窗口调试控制台上的其实都是字符串),OJ时是通过判断你输出的字符串是否和后台文件中的字符串一致,差一点都无法AC。必须一模一样才能AC,所以务必掌握输出格式(机试很重要)。
如果没有控制输出格式,则浮点数98.5会输出98.500000,OJ想要AC必须和要求输出的格式一模一样,即输入98.5,输出也必须是98.5才行。
控制输出格式后:
//printf控制输出格式
int main() {
//将score=%f修改为score=%4.1f,4表示浮点数输出占4个位置(包括小数点),1表示小数点后有1位
printf("name=%s,age=%d,sex=%c,score=%4.1f\n", "LXL", 22, 'm', 98.5);
}
//将score=%f修改为score=%0.5f,5表示小数点后有5位
printf("name=%s,age=%d,sex=%c,score=%0.5f\n", "LXL", 22, 'm', 98.5000);
以下是整型数和字符串控制输出格式,没有那么重要。
//printf控制输出格式
int main() {
//将score=%f修改为score=%4.1f,4表示浮点数输出占4个位置(包括小数点),1表示小数点后有1位
printf("name=%4s,age=%-3d,sex=%c,score=%4.1f\n", "LXL", 22, 'm', 98.5);
}
解答疑问
内存地址解析
VS中内存调试窗口显示四个字节还是显示是几个字节,是拖动的效果。
这里32位程序,所以是32位地址总线和32位数据总线,所以让内存取4B。(事实上我们的计算机现在都是64位的机器,但考试只考32位的机器,二者原理是一样的)。
32位机器
运算符
运算符类型
C语言提供了13种类型的运算符:
- 算术运算符(+ - * / %)【掌握】
- 关系运算符(> < == >= <= !=)【掌握】
- 逻辑运算符(! && ||)【掌握】
- 位运算符(<< >> ~ | ^ &)【了解,在高级阶段会学】
- 赋值运算符(=及其扩展赋值运算符)【掌握】
- 条件运算符(? 😃【了解】
- 逗号运算符(,)【了解】
- 指针运算符(*和&)【在指针部分学习】
- 求字节数运算符(sizeof)【掌握】
- 强制类型转换运算符((类型))【掌握】
- 分量运算符(. ->)【在结构体部分学习】
- 下标运算符([ ])【在数组部分学习】
- 其他(如函数调用运算符( ))
从这里可以知道sizeof其实是运算符,而不是函数。
双目运算符:需要有两个操作数。
算术运算符包含+、-、*、/和%,当一个表达式中同时出现这5种运算符时,先进行乘(*)、除(/)、取余(%),取余也称取模,后进行加(+)、减(-),也就是乘、除、取余运算符的优先级高于加、减运算符。
算术运算符的优先级见下图。C语言所有的运算符的优先级见附件[C语言优先级完整版]。
处于同一级别的优先级一样,若同时出现则运算先后次序是从左到右;1比2的优先级高,先计算1后计算2。
**常用的运算优先级:逻辑非 ! > 算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符 = **.
算术运算符练习
int main() {
scanf("%d", &a);
printf("a=%d\n", a);
/*
如果输入的是1234,那么现在将让输出4321,该如何操作?
思路如下:1234%10=4,余数是4,商是123;得到的商再重复%4,余数得到3,商是12,重复上述步骤;
当商为0时,跳出循环.(最后是1%10,余数是1,商是0,此时可以结束循环了)。
*/
//对于循环结束条件不知道如何写,可以先写循环体,等循环体写好了,循环结束条件也就大概知道了
while (a!=0) //当a不等于0时就可以进入循环
{
printf("%d", a % 10);
a = a / 10;
}
}
拓展:上述结果如何以字符形式输出4321?
只需将循环体改成如下内容即可,因为查ASCII码表可知,字符0-9对应十进制48-57。所以整型数+48再以字符形式输出即可。
while (a!=0)
{
//以字符形式输出结果4321
printf("%c", a % 10 + 48);
a = a / 10;
}
关系运算符
关系运算符> < == >= <= != 。由关系运算符组成得表达式称为关系表达式,关系表达式只有真和假,对应得值为1和0。由于C语言中没有布尔(bool)类型,所以在C语言中0值代表假,非0值代表真。
【注】C语言认为一切非零值都是真。
if (5)
{
printf("true");
}
//C语言认为一切非零值都是真。所以会输出ture
//C语言中没有布尔(bool)类型,所以以下结果并不会输出true 或 false,而是输出1 或 0
printf("%d\n",5 < 3);//结果为假,输出0
printf("%d\n", 5 > 3);//结果为真,输出1
逻辑运算符
概述
! 逻辑非 :如果原来是真,取非就是假;如果是假,取非就是真。
&& 逻辑与 :a&&b,a和b任何一个为假,就是假;如果均为真,整体为真。
|| 逻辑或 :a||b,a和b任何一个为真,则结果为真;如果两个都为假,则结果为假。
逻辑运算符组成的式子叫逻辑表达式,逻辑表达式只有真和假,对应的值就是1和0。
//关系运算符和逻辑运算符
int main(){
int a = 8;
//常用的运算优先级:算术运算符>关系运算符>逻辑运算符。所以 if的判断条件不用加括号
if (a > 3 && a < 10)//如果要判断3<a 同时 a<10 ,要用逻辑运算符
{
printf("a is right");
}
else {
printf("a is wrong");
}
}
练习:判断浮点数是否等于某个值(判断两个浮点数是否相等)。不太重要,初试没有考过浮点数,复试也不太可能考这个点,因为这是个细节点,除非复试特别难,才会这样考。
【注】当一个浮点数以IEEE754标准存储到计算机中时,其实是“近似的”。浮点数在计算机中的存储是近似存储,可以这样理解,是用两个数相乘去逼近这个浮点数的。
通过监视调试窗口可以看到f1赋值234.56,但在计算机中存储的确是234.559998,所以当用f1 == 234.56 时返回值是0,说明为假。(其实计算机是拿234.559998 与 234.560000去比较,结果当然为假)
//判断浮点数是否等于某个值(判断两个浮点数是否相等)
int main(){
/*
浮点数在计算机中的存储是近似存储,可以这样理解,是用两个数相乘去逼近这个浮点数的。
*/
float f1 = 234.56;
if (f1 == 234.56) //判断结果返回是0,说明为假
{
printf("f1 is equal to 234.56\n");
}
else {
printf("f1 is not equal to 234.56\n");
}
//判断浮点数是否等于某个值(判断两个浮点数是否相等),必须用以下方法
float f2 = 234.56;
if (f2 - 234.56 > -0.0001 && f2 - 234.56 < 0.0001)//判断结果返回是1,说明为真
{
printf("f2 is equal to 234.56\n");
}
else {
printf("f2 is not equal to 234.56\n");
}
}
以上代码的输出结果如下图:
从以上代码中可知,判断浮点数是否等于某个值(判断两个浮点数是否相等),必须用以下方法:
if (f2 - 234.56 > -0.0001 && f2 - 234.56 < 0.0001)
原理是判断是否是一个“近似值”即可,取0.0001是因为浮点数显示的有效位数一般为7位(这里好像又与IEEE754描述的有效位数不太一样,这里记住怎么用就行,不太重要)。原理就是判断两个浮点数在数轴上的距离是否小于0.0001。
数据结构中的大题大概率会用到算术运算符,关系运算符,逻辑运算符;如写排序时就会用到这三种运算符。
一般OJ题目中限制输入范围是为了降低难度,并不是要求代码中实现只能输入该范围的数,除非题目说明当输入的值不在该范围时输出字符串“wrong”或“error”。
逻辑非
首先说明一点,逻辑非用到的地方不多。
针对代码中的逻辑非,首先给变量j赋值5,因为j的值非0,所以!j的值为0;然后由于逻辑非是单目运算符,结合顺序是从右至左,得到!!j的值为1。也就是对0取非,得到的值为1;对非0值取非,得到的值为0。【逻辑表达式只有真和假,对应值是1和0,在C语言中非0值都为真】
//逻辑非
int main(){
int i, j, k;
j = 5;
i = !j;
printf("i=%d\n",i);
k = !!j;
printf("k=%d\n", k);
}
赋值运算符
**常用的运算优先级:逻辑非 ! > 算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符 = **.
while (rewind(stdin),(ret=scanf("%d",&i))!=EOF)//while是实现循环,后面要有一个小括号。当为true时进入循环。
{
printf("i=%d\n", i);
}
(ret=scanf(“%d”,&i))!=EOF 这里赋值运算符的优先级低于关系运算符,所以要想让赋值运算符先运算,需要加小括号()。[我们这里希望scanf的返回值先赋给ret,然后再用ret与EOF进行判断]
赋值运算符的左边只能是变量,以下代码会报”左操作数必须为左值“的错误。
//赋值运算符的左边只能是变量
int main(){
int a = 5;
//a + 3 = 10;
//如果报出左操作数必须为左值,这是说明等号左边必须是变量。
//左值:可以修改的值,也就是说只有变量才能叫左值
}
逗号运算符
逗号运算符的优先级最低。逗号表达式的整体值是最后一个表达式的值。
while (rewind(stdin),(ret=scanf("%d",&i))!=EOF)//while是实现循环,后面要有一个小括号。当为true时进入循环。
{
printf("i=%d\n", i);
}
while中rewind(stdin),(ret=scanf(“%d”,&i))!=EOF的”逗号,“就是逗号运算符。逗号前后各一个表达式,整体构成一个逗号表达式,逗号表达式的运算顺序是从左至右,逗号表达式的整体值是最后一个表达式的值,最后一个表达式为真,则整体为真,最后一个表达式为假,则整体为假。一般用逗号表达式往往都是在while循环里面或for循环里面,其他场景不用。
自增、自减运算符
任何时候,遇见i++;i–等都拆成两步。如j = i++ > -1;拆成两步,j=i>-1;i++。再如j = i++;拆成两步,j = i ; i++。
++i ; --i 按优先级进行正常运算即可。如 j = ++i ; 相当于 j = i +1。
自增自减运算符的结合顺序是从右到左,解释一下:逻辑非的结合顺序也是从右到左,从右到左就是拿操作数与运算符操作,如 !!j 结合顺序是从右到左,顺序是 !(!j) , 也就是从右边开始依次结合。而算术运算符都是从左到右,如 i + j + k,结合顺序是从左到右,顺序是(i + j) + k ,也就是从左边开始依次结合。
//i++代表的是i=i+1;比较难理解的是后加加和后减减
int main(){
int i = -1;
int j;
j = i++ > -1;//拆成两步,j=i>-1;i++
printf("i=%d,j=%d\n", i, j);
}
求字节运算符
sizeof不是函数,是C的关键字,是一个运算符。求字节运算符sizeof( )中只能放变量,不能放表达式。
int main(){
int i = -1;
//求字节运算符sizeof( )中只能放变量,不能放表达式
printf("i的字节数=%d\n", sizeof(i));
//控制台输出 i的字节数=4
}
作业练习
Description:读取一个65到122之间的整型数,然后以字符形式输出它,比如读取了97,输出字符a。Input:读取一个整型数,整型数 大于等于65,小于等于122;Output:输出整型数在ASCII表中对应的字符。
int main(){
int i;
scanf("%d", &i);
printf("%c\n", i);//整型数在0-128之间可以用%c输出。实际上就是ASCII码表
return 0;
}
下面来演示一下一个经典的错误。当实际操作的空间超出了变量本身占用的空间大小时,就会报这种错误。这个错误是很经典的,后面学数组时依然有可能会犯这个错误。