当梦拓了,想 push 我的 xdx 们学 C 语言,遂有此文(不过大家好像也没好好学 QAQ )
C语言quiz(二)
by 西门蒸馍
考点
- for 循环、循环控制
- 数据类型
- 整数补码
- 整数范围
- 字符类型
- 类型转化
- 位运算
- 自定义函数
- 生存周期 & 作用域
- 数组
题目
- 以下哪个循环应用场景最适合使用
for
循环
( )
A. 计算从 1 到 n 的倒数和
B. 使用
scanf
循环输入整数,直到读到文件末尾EOF
C. 构建一个死循环(循环条件值为 1 ),并在合适的时候
break
D. 在进入循环前需要首先执行一次循环体
- 以下哪个循环应用场景最适合使用
答案&解析
A【解析】
A. for
循环适合做遍历操作
B. 一般的写法是 while( scanf("%d",&n)!= EOF )
C. 一般的写法是 while(1){ if(...) break; }
D. 最适合使用 do{}while(...);
循环
PS: 本题没有绝对化,只讨论符合大众逻辑的代码风格,while
和 for
两种循环实际上完全互通,如何选择全看个人习惯
- 以下关于整数数据类型的说法正确的有[多选]
众所周知,计算机是以二进制的方式存储数据的,最小的单元为比特(bit),一个比特只能代表
0
或1
,做此题前,请先简要了解一下“字节”和“比特”( )
A. 常见的整数数据类型有
char
、short
、int
、long
、long long
B. 一个
char
类型的整数大小为一个字节(byte),一个字节占 6 个比特(bit)C. 在计算机上一个
int
类型的整数固定占 4 个字节D. 计算机中
int
类型默认为signed int
有符号整数,char
类型默认为unsigned char
无符号整数E.
unsigned int
表示无符号的整数数据,只能表示非负数,但表示正数的最高上限约是signed int
的二倍F. C 语言中可以用
sizeof()
查看变量或数据类型的比特数
答案&解析
AE【解析】
B. 一个字节占 8 bit
C. 在不同类型的计算机上,int
的大小不同:目前可以简单认为,在 16 位的编译器上,int
占 2 字节;在 32 位的编译器上,int
占 4 个字节。
D. C 语言中并没有规定 char
是有符号整数还是无符号整数,不同计算机或编译器上可能不同。
F. sizeof()
查看的是字节数
- 以下关于计算机对整数的编码方式的说法中正确的有[多选]
( )
A. 计算机对整数的编码方法有原码、反码、补码,目前计算机采用的是补码
B. 采用补码的优点在于可以让计算机只通过二进制下的”加”,就可以完成整数范围内的加减运算
C. 若一个
unsigned char
类型的整数的补码为10100010
,则该整数为 $2^2 + 2^6 + 2^8 = 324$D. 对于
signed char
的类型的数据,其能表示的最大的数的补码为01111111
,最小的数的补码为10000000
E. 有符号整数的最高比特位为
0
则说明表示的是正数,最高比特位为1
表示的是负数
答案&解析
ABD【解析】
C. 二进制最小位是第 0 位 ,代表 $2^0$ ,该整数的值应为 $2^1 + 2^5 + 2^7 = 162$
E. 有符号整数的最高比特位为0
表示非负数
- 以下关于整数范围的说法正确的有[多选]
本题中的 “a ~ b”,表示闭区间 [a,b]
( )
A. 通常来讲,整数类型占用的字节越多,可表示的数据范围就越大
B. 4 字节的
int
类型可表示 $-2^{32}$ ~ $2^{32}-1$ 之间的数据C.
unsigned char
类型可以表示 $0$ ~ $2^{8}$D. 若
int
类型的变量爆了int
范围(即超过int
可表示的数的上限),例如,int a = 0; while(1) a++;
则当a
超过了int
的上限时,a
会变为最小负数,继续自增
答案&解析
AD【解析】
B. 4 字节的 int
类型可表示 $-2^{31}$ ~ $2^{31}-1$ 之间的数据
C. unsigned char
类型可以表示 $0$ ~ $2^{8}-1$
D. 如第 3 题 C 选项所述,char
类型的数最大为 01111111
,再自增后就变成了 10000000
,从最大正数变为了最小负数,int
类型也一样
- 在控制台中输入
c d
,以下代码的输出结果为
如有需要,请自行查询 ASCII 码表
题目中的
[space]
表示字符空格( )
1
2
3
4
5
6
7
8
int main(){
char ch1,ch2;
scanf("%c%c",&ch1,&ch2);
printf("%c%d",ch1,ch2);
return 0;
}A.
cd
B.
c100
C.
c[space]
D.
c32
- 在控制台中输入
答案&解析
D【解析】
- 输入
c[空格]d
实际上输入了 3 个字符,分别是'c'
、'[space]'
、'd'
,scanf
函数会按输入顺序读取字符'c'
和字符'[space]'
,字符d
被丢弃了,所以实际上ch1
存储了字符'c'
,ch2
存储了字符'[space]'
。printf
函数先输出ch1
的字符,再输出ch2
的 字符的ASCII 码,所以最后输出结果为c32
。 - 这也是大家常犯的错误,用
scanf
读取数据时,一定考虑到输入数据的格式与格式化字符串(就是scanf
引号中的内容)。对于本题来说,如果要读入字符'c'
和字符'd'
的话,可以这样写:scanf("%c %c",&ch1,&ch2);
,也就是让scanf
把输入进来的空格先读走,再读取字符d
。除了空格符以外,换行符'\n'
,也很容易造成 IO 问题(input 和 output,也就是输入输出问题),大家以后会学到更多 C 语言的 IO 知识,处理更复杂的 IO 问题。
- 下列代码语句执行过后,
c
的值是5546261880000
的选项有[多选]
已知数学中乘法式 237324 * 23370000 = 5546261880000
平时出现在源代码中的整数都默认为
int
类型的,1LL
表示long long
类型的 1 ,等价于(long long)1
( )
A.
1
2int a = 237324, b = 23370000;
long long c = a * b;B.
1
2int a = 237324, b = 23370000;
long long c = 1LL * a * b;C.
1
2int a = 237324, b = 23370000;
long long c = a * 1LL * b;D.
1
2int a = 237324, b = 23370000;
long long c = a * b * 1LL;E.
1
2int a = 237324, b = 23370000;
long long c = (long long)(a * b);F.
1
2int a = 237324, b = 23370000;
long long c = (long long)(a) * b;G.
1
2int a = 237324, b = 23370000;
long long c = a * (long long)(b);- 下列代码语句执行过后,
答案&解析
BCFG【解析】
A 选项首先是错的。我们知道赋值运算实际上是先计算右值(等号右边的表达式),再赋给左值。在本题中,右值为
a*b
,a
和b
均为int
类型, 右值计算的结果也是一个int
类型的数据,其值本应该为5546261880000
。但很显然,这个数值超过了int
的上限 (爆了int
),出现这种情况时,程序会保留5546261880000
二进制编码的前 32 位(int
类型占据的比特数,假设int
占 4 字节),将其作为a*b
的值,并丢弃之后的比特位(long long
类型的值占 64 比特位,所以丢弃后 32 个比特),实际得到的值为1459100864
。如下图所示。首先给大家回忆一下默认的类型转换。我们之前知道
double
类型的数与int
类型的数参与运算时,int
会被默认转为double
进行运算 ,例如我们把int
类型的整数a
转为double
类型的值, 既可以直接(double)a
,又可以a*1.0
。而long long
类型与int
类型同样满足这样的方法,所以一个int
的数* 1LL
后就成为一个long long
类型的数,不存在 A 选项的情况,这样选项 B、C 就是正确的- 至于 D 选项,由于同优先级满足从左向右的计算规律,所以本质上是
(a*b)*1LL
,a*b
先计算,出现了类似 A 选项中的情况,所以a*b*1LL
的结果为long long
类型的1459100864
- E 选项与 D 类似,F、G很好理解
填空题
0x000000FF
代表一个前 8 比特位全为1
,后 24 比特位全为0
的int
类型的整数(1)
0x000000FF & 256
的值为 0(2)
n&1
的值为 0 ,说明整数n
为 偶数 (填 “奇数” 或 “偶数” )(3)想让整数
n
第 0 比特位的值为1
,应该可以使用某位运算符?
。若n?1
可以实现该效果,则位运算符?
应该为|
(填&
、|
、^
、>>
、<<
或~
)(4)对于任意有符号整数
n
,n + ~n
的值为 -1(5)想要一个整数乘以 $2^n$ ,假设不超出数据范围,比较快的方法是进行 左移 运算(填 “左移” 或 “右移” )
(6)负整数进行右移运算时,最高位会补上比特 1 (填 “ 0 ” 或 “ 1 ” )
答案&解析
0 偶数 | -1 左移 1
【解析】
(1)256
的二进制码只有第 8 个比特位为 1 ,其余都是 0 (比特位从 0 开始计数),而0x000000FF
第 0 ~ 7 比特位为 1 ,其余为 0 ,按位与 之后得到 0
(2)较好理解,记住就行
(3)按位或 可以进行置 1 操作
(4)举个例子:二进制码 01010101
按位异或 后得到 10101010
,相加为 11111111
,即为数 -1
(5)举个例子:无符号整数二进制码 01010101
的数值为 ,左移后得到 10101010
,其数值为 ,变为了 2 倍。
(6)记住就行,右移运算时,若最高位为 1 则补 1 ;最高位为 0 则补 0
- 下列代码可以正常编译的有[多选]
( )
A.
1
2
3
4
5
6
7
8
9
10
11
12
13
int isEven(int x){
if( x % 2 == 0 ) return 1;
else return 0;
}
int main(){
int x;
scanf("%d",&x);
if( isEven(x) ) printf("This is an even number\n");
else printf("This is an odd number\n");
return 0;
}B.
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(){
int x;
scanf("%d",&x);
if( isEven(x) ) printf("This is an even number\n");
else printf("This is an odd number\n");
return 0;
}
int isEven(int x){
if( x % 2 == 0 ) return 1;
else return 0;
}C.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int isEven(int x);
int main(){
int x;
scanf("%d",&x);
if( isEven(x) ) printf("This is an even number\n");
else printf("This is an odd number\n");
return 0;
}
int isEven(int x){
if( x % 2 == 0 ) return 1;
else return 0;
}D.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int isEven(int);
int main(){
int x;
scanf("%d",&x);
if( isEven(x) ) printf("This is an even number\n");
else printf("This is an odd number\n");
return 0;
}
int isEven(int x){
if( x % 2 == 0 ) return 1;
else return 0;
}
答案&解析
ACD【解析】
- 代码编译是从前往后编译的,
main
中调用的自定义函数需要提前定义函数体(如选项 A )或 事先声明函数的返回值、函数名、参数表(如选项C、D),函数声明时可以舍去参数名(如选项 D),但必须保留参数类型和正确的传参顺序 - C 语言中不是特别强调函数及变量的定义( definition )与声明( declaration ),但最好自我查询并理解二者的区别
- 下列有关变量的生存周期和作用域的说法正确的有[多选]
( )
A. 全局变量的生存周期从程序执行开始到程序运行结束,且可以被所在文件中的所有函数访问到
B. 自定义函数传入的形参只在函数被调用的过程中存在
C. 在本层作用域中定义变量时,不可选用外层作用域中出现过的变量名
D. 下列代码可正常编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main(){
int a;
scanf("%d",&a);
switch (a){
case 1:
printf("a euqals 1");
int b = a;
break;
case 2:
printf("a equals 2");
int b = a;
break;
default:
printf("none of 1 or 2");
break;
}
return 0;
}
答案&解析
AB【解析】
C. 可以选择外层出现过的变量名。在本层中,定义的变量名会覆盖外层的同名变量,且不会对外层变量产生影响。但是,理论是理论,实践中写代码极其不推荐这么干,不然自己都搞不清。
D. 编译不会通过,是因为本质上两次int b = a;
都是在同一作用域中,会出现重复定义的错误。其实以后你们还会遇到许多在 switch
语句中定义变量报错的情况,实在解决不了,就改用if...else if...
吧
- 下列关于数组的说法正确的有[多选]
( )
A. 遍历一个长度为
10
的数组a[10]
,可以for(int i = 0; i <= 10; i++) // do something
B. 定义一个数组时必须指明数组的长度,定义之后数组长度无法改变
C.
c99
标准以后,变量可以作为数组长度用来定义一个数组D. 数组作为参数传入自定义函数中,函数内对数组的操作也会影响到函数外部的数组
答案&解析
BCD【解析】
A. 新手常犯的错误, for(int i = 0; i <= 10; i++) // do something
这样实际上循环了 11 次,会出现数组越界,应该把 <=
改成<
C. 虽然是对的,但是不建议用变量作为数组长度
D. 学习到指针之后,大家会了解到数组的本质,就可以理解了
是不是不会做?不会做就对了!如果你能把 quiz2 的知识完全掌握,说保守一点,你在开学时就把大概90%的同学甩在身后了。如果不会做也没关系,慢慢学习就可以了,让一年前的我做这些题,我根本无从下手(QAQ)。quiz2 不需要正确率有多高,只要看完答案解析能够大致懂一点就可以了。
quiz1 和 quiz2 虽然都是选填题,但是其中出现了好多你们以后会写出来的 bug ,如果以后 debug 实在 de 不出来,不妨翻翻这两个 quiz ,说不定会有思路。暑期的程设预习就帮助到这里了,希望大家开学以后天天 AC 没有 WA 、TLE、MLE … 捏。