似水流年 | C程价值题归档
基本信息
授课教师: yhj
上课教材: 用不着
编译环境: gcc version 13.1.0 (x86_64-win32-seh-rev1, Built by MinGW-Builds project)
参考资料: C小程 琐碎知识点整理 | C 语言应试笔记 | 《C 陷阱与缺陷》笔记 | 何钦明PTA答案
P.S. 感恩学长学姐们的无私分享
MISC
-
设有声明:
char *s1="xyz",*s2="123",t1[10],*t2;
则能完成字符串 s1 和 s2 的值交换选项有( C )A. t1=s1;s1=s2;s2=t1;
B. strcpy(t1,s1);strcpy(s1,s2);strcpy(s2,t1);
C. t2=s1;s1=s2;s2=t2;
D. strcpy(t2,s1);strcpy(s1,s2);strcpy(s2,t2);解释: 首先要明确题干中变量的类型,s1,s2以及t2的类型都是字符型指针,而t1的类型是字符数组(C语言没有字符串类型,题干中的字符串指的应该是抽象的字符串)
然后我们逐个选项观察,A选项试图将s1赋值给t1,这个操作就是不合法的。在C中数组(不论是什么数组)是不可作为左值进行赋值操作的,因此A直接pass;
B选项使用了string.h库中的strcpy函数,这个函数接受两个字符型指针char*,从前者的’\0’开始,把后者连带着’\0’拷贝到前者。但是strcpy要求目标指针(也就是前者)是可变的,而s1是不可变的,因此运行时会在第二句语句报出段错误Segmentation fault;
C选项没有问题,C语言虽然不支持字符型指针向字符数组赋值,但是支持字符型指针向字符型指针赋值,因此s1("xyz"的首元素的地址)赋给了t2并完成接下来的交换操作;
D选项不合法之处与B类似,不再赘述。
这道题涉及到了很多东西,对C的理解更上一层楼,不失为又新又好。 -
以下程序的输出结果是( ONALAMB )。
typedef char (*AP)[5];
AP defy(char *p)
{
int i;
for(i=0; i<3; i++)
p[strlen(p)] = 'A';
return (AP)p + 1;
}
void main()
{
char a[]="FROG\0SEAL\0LION\0LAMB";
puts( defy(a)[1]+2 );
}解释: 行3 首先定义了一个新的变量类型AP表示一个大小(注意不是字节)为5的字符型指针。函数defy接受一个字符型指针,重复三次以下操作:
p[strlen(p)]='A';
,这句话的意思是将p所指向的字符串的后导’\0’替换为’A’,最后将p强制类型转换为AP(即p变成了逻辑上的大小为n*5的二维字符数组)并+1(意思即是指向下5个字符)。
接下来看主函数部分,定义了字符数组a,初始化为由’\0’分隔的4个4个字符,接下来将a传入defy函数,根据上面的分析我们可以知道a的值被修改为了"FROGASEALALIONALAMB",接下来p(注意修改p不会影响a,指针的传参本身也是传值的)被强转为AP,于是p的逻辑结构变为{“FROGA”,“SEALA”,“LIONA”,“LAMB”}(注意只是逻辑结构,因此前面三个字符串并没有后导’\0’)。接下来p+1返回的便是以"SEALA"的’S’的地址为值的一个n*5的二维字符数组。
最后在puts函数里面,返回的defy(a)被取了第1个字符串即"LIONA"的首地址后又向右移动了两个字符大小的地址,即指向了’O’,于是puts会打印从’O’到其后的第一个’\0’前的所有字符,即ONALAMB。
这道题要求我们十分清楚每个变量在每个操作后的类型,搞明白类型后就简单了。 -
关于变量声明的解读方法,以
int **a[2][3];
为例:
采用从a开始,向右向左看,组词造句的方法 | 此处参考- a is … int;
- a is array of 2 … int;
- a is array of 2 of array of 3 … int;
- a is array of 2 of array of 3 pointer(s) to … int;
- a is array of 2 of array of 3 pointer(s) to pointer(s) to int;
- a 是一个2*3的数组,每个数组元素都是一个指向整型指针的指针
作为习题,请读者分析一下以下声明中,a的类型和a的字节数(64位操作系统):
static char *(*(**a[2][3])())[8];
key: a is array of 2 of array of 3 pointer to pointer to function returning pointer to array of 8 pointer to static char.
sizeof(a) = 48 = 2 * 3 * 8 -
关于特殊转义符
转义序列 含义 \a BEL(响铃) \b BS(退格) \f FF(水平制表符) \n LF(换行) \r CR(回车) \t HT(制表符) \v VT(垂直制表符) \\ \ \’ ’ \" " \0 NULL \ddd 八进制 \xhh 十六进制 最后两个表示对应进制下用ASCII码表示的字符
0919
- 程序调试就是找出并改正C源程序中的语法错误。(F)
- 上机运行以下程序(例1-1),输入整数13,输出结果是错误的,其原因是( )。 运算结果超出了整数的取值范围
int main(void) /* 主函数 */
{
int n; /* 变量定义 */
int factorial(int n); /* 函数声明 */
scanf("%d", &n); /* 输入一个整数 */
printf("%d\n", factorial(n)); /* 调用函数计算阶乘 */
return 0;
}
int factorial(int n) /* 定义计算 n! 的函数 */
{
int i, fact = 1;
for(i = 1; i <= n; i++){
fact = fact * i;
}
return fact;
}
解释:
0926
- 执行以下程序段,输入1.0 0.01 365,输出1.0#0.010#365。(T) 解释: 在printf()里面,%f等价于%lf,因此不会出错(scanf()会出错)
int day;
double factor, initial;
scanf("%lf %lf %d", &initial, &factor, &day);
printf("%.1f#%.3f#%d", initial, factor, day); - 执行以下程序段,并回答下列问题。请注意,直接填数字,前后不要加空格等任何其他字符。 循环体语句共执行了(0)次
int fahr;
double celsius;
for (fahr = 121 ; fahr <= 125; fahr++) ;
celsius = 5.0 * (fahr - 32) / 9.0;
printf("%4d%6.1f\n", fahr, celsius);
当循环结束时,变量fahr的值是(126)
解释: 无聊粪题的代表
1007
- 为了检查以下if-else语句的两个分支是否正确,至少需要设计3组测试用例,其相应的输入数据和预期输出结果是( ) 。 输入3和4,输出4;输入5和5,输出5;输入4和3,输出4。
int x, y;
scanf("%d%d", &x, &y);
if(x >= y){
printf("max = %d\n", x);
}else{
printf("max = %d\n", y);
}
解释: 要让x大于,等于,小于y的情况都出现一遍
1010
-
if-else语句的一般形式如下,其中的语句1、语句2只能是一条语句。(T)
if (表达式)
语句1
else
语句2解释: 语句块视作多条语句,因此只能是一条语句
-
省略else的if语句的一般形式如下,若表达式的值为“真”,则执行语句1;否则,就什么也不做。(T)
if (表达式)
语句1解释: 省略else是真没有else,因此程序就是孤零零的if,文字游戏的粪题
-
如果变量已经正确定义,则执行以下程序段后,x的值不变。(F)
x = 4;
if (x < 0){
y = -1;
}else if (x = 0){
y = 0;
}else{
y = 1;
}解释: 注意行4 是赋值运算符,因此x会被赋值为0,y会被赋值为1
-
执行语句putchar(‘S’);后,在屏幕上显示的输出结果是’S’。(F)
解释: 输出的结果没有引号 -
设变量已正确定义,执行以下程序段,顺序输入三个字符’Q’,则输出Q。(F)
ch = getchar();
putchar(ch);解释: 跑出来的结果是对的(输出Q),暂时不知道出题人玩的什么文字游戏,或者他用的什么古早编译器,当成对策题记住好了
-
写出以下程序段的运行结果。请注意,直接填单词、字符或者两者的组合,前后不要加空格等任何其他字符。
double grade;
scanf ("%lf", &grade);
if(grade < 60); {
printf("Fail");
}
printf("?");输入50,输出( Fail? )
输入90,输出( Fail? )
解释: 眼力题,注意行3 后面有个分号,出题人大概是没活整了 -
写出以下程序段的运行结果。请注意,直接填单词,前后不要加空格等任何其他字符。
mynumber = 38;
scanf ("%d", &yournumber);
if(yournumber == mynumber){
printf("Right");
}
if(yournumber > mynumber ){
printf("Big");
}else{
printf("Small");
}输入20,输出small
输入38,输出(RightSmall)
输入50,输出(Big)
解释: 眼力题,注意行6 的if前面没有else,因此输入38会输出两个结果
1017
- while语句的一般形式如下,其中的循环体语句只能是一条语句。(F) 解释: 同1010-1
while (表达式)
循环体语句 - 写出以下程序段的运行结果。请注意,直接填数字、单词或者两者的组合,前后不要加空格等任何其他字符。 输入11+1,输出(12Error10)
char op;
int value1, value2;
scanf("%d%c%d", &value1, &op, &value2);
switch(op){
case '+': printf("%d", value1 + value2);
default: printf("Error");
case '-': printf("%d", value1 - value2);
}
输入10$100,输出(Error-90)
解释: switch-case等价于goto,没有break正常往下执行 - 若变量已正确定义,写出以下程序段的运行结果。 输入2,输出( 0.00# )
scanf ("%lf", &eps);
i = 0;
flag = 1;
denominator = 1;
item = 1.0;
s = 0;
while(fabs(item) >= eps){
item = flag * 1.0 / denominator;
s = s + item;
i++;
flag = -flag;
denominator = denominator + 2;
}
printf ("%.2f#%d\n", s, i);
输入1,输出( 0.67# )
输入0.2,输出0.72#
解释: 我不知道考试的时候怎么算这种鸟题 - 若变量已正确定义,写出以下程序段的运行结果。 输入1 2 3 0 -1,输出( 2#3#0#-1# )
scanf ("%d", &k);
while(k >= 0){
scanf ("%d", &k);
printf("%d#", k);
}
输入1 2 3 -1 9,输出( 2#3#-1# )
解释: 注意scanf重复了,因此第一个值会被丢弃,并且判断在输出之后,因此第一个负值照常输出
1024数组
- 数组定义后,只能引用单个的数组元素,而不能一次引用整个数组。(F)
解释: 出题人的意思是没有array[0:100]的操作,那的确没有 - 如果变量定义如下,则正确的语句是(BCE)。 解释: 我不知道出题人对“正确”是个什么定义,如果是过编,那不好意思,都能过编。上面是题外话,C语言会给数组赋初值0,因此C没问题,AD都越界了
int k, a[10];
A. a[-1] = -1;
B. a[0] = 23;
C. k = 3;
a[k - 2] = a[9] + 1;
D. for(k = 1; k <= 10; k++){
printf("%d ", a[k]);
}
E. for(k = 0; k < 10; k++){
scanf("%d ", &a[k]);
} - 在以下描述中,(ABCE)是正确的。 解释: 原因同1024数组-2
A. int a[5] = {1, 2, 3, 4, 5};
定义了数组a,并对数组元素赋初值。此时,a[0]为1,a[1]为2,a[2]为3,a[3]为4,a[4]为5。
B. static int b[10];
定义了静态数组b,且10个数组元素的初值都为0。
C. int fib[45];
定义了数组fib,且45个数组元素的值都为0。
D. static int week[7] = {1, 2, 3};
定义了静态数组week,并对数组 week 的前3个元素week[0]~week[2]赋初值,week[3]~week[6]值都是不确定的。
E. int cnt[10] = {1};
定义了数组cnt,并对cnt[0]赋初值1,其余元素的初值为0。
1031
- 以下程序段的功能是输出1~100之间每个整数的各位数字之和。(F) 解释: 应该是每个数字的逆转数
for(num = 1; num <= 100; num++){
s = 0;
do{
s = s + num % 10;
num = num / 10;
}while(num != 0);
printf("%d\n", s);
}
P.S.1031的题目集简直是粪题大赏,实在不想看,这里省略
1107
- 08是正确的整型常量。(F)
- 表达式0195是一个八进制整数。(F)
- 下面程序段的输出是(18)。 解释: 在C语言里面,字符插入整型表示一些特殊进制或计数法:
int x=023;
printf("%d\n",--x);08是八进制,不能有8//前缀
printf("%d\n",0b10); //2
printf("%d\n",010); //8
printf("%d\n",10); //10
printf("%d\n",0x10); //16 或是0X10
printf("%lf\n",2e3); //2000.000000
//后缀 LU不敏感大小写,也可以改变顺序
printf("%ld\n",123L); //长整型 long (int)
printf("%lld\n",123LL); //长长整型 long long (int)
printf("%u\n",123U); //无符号整型 unsigned (int)
printf("%llu\n",1LLU); //无符号长长整型 unsigned long long (int)
//注意:C只规定了sizeof(short)<=sizeof(int)<=sizeof(long)<=sizeof(long long)
//具体占用多少字节及数据范围由编译器决定
//我的编译器分别是2 4 4 8 - 在C语言程序中,若对函数类型未加显式说明,则函数的隐含类型为(int)。
解释: C是int,C++是void - 以下正确的函数定义形式是()。
double fun(int x, int y)
解释: 牛角尖大赏,定义不能有分号,声明可以
1114
-
执行语句int *p = 1000;后,指针变量p指向地址为1000的变量。(F)
解释: 编译报错,即使改为int* p=(int*)1000;访问时依然会出现段错误 -
表达式 (z=0, (x=2)||(z=1),z) 的值是1。(F)
解释: 逗号运算符,从左往右,返回最后一个表达式的值;||具有短路求值的特性,因此z=1没有执行 -
运算符( )的优先级最高。
解释:
参考:C 运算符优先级类别 运算符 结合性 后缀 () [] -> . ++ -- 从左到右 一元 + - ! ~ ++ – (type) * & sizeof 从右到左 乘除 * / % 从左到右 加减 + - 从左到右 移位 << >> 从左到右 关系 < <= > >= 从左到右 相等 == != 从左到右 位与 AND & 从左到右 位异或 XOR ^ 从左到右 位或 OR | 从左到右 逻辑与 AND && 从左到右 逻辑或 OR || 从左到右 条件 ?: 从右到左 赋值 = += -= *= /= %=>>= <<= &= ^= |= 从右到左 逗号 , 从左到右 那本没用教材里面的表格比较细致,有个大概印象就行,要注意的是:
- 逻辑非和按位非优先级特别高
- 后缀的+±-是i++这样的,一元的+±-的–i这样的
- 一元类别里面的±是正负号,&是取址,*是取值,(type)是强转类型,sizeof是取类型的字节数
- 表示下标的>对单个数操作的>数字运算>移位>关系>位运算>逻辑运算>条件运算>赋值运算>逗号
-
假设整型数据用两个字节表示,则用二进制表示-127的原码为(11111111)、反码为(10000000)、补码为(10000001)
解释:数字类型 原码 反码 补码 正数 43 00101011 相同 00101011 相同 00101011 负数 -43 正数原码,符号位置1 10101011 除符号位取反 11010100 反码加1 11010101 +0 00000000 00000000 00000000 -0 10000000 11111111 加一后溢出 00000000 0的补码唯一,补码可以直接相加
-
写出以下程序段的运行结果。
int a = 2, b = 0, c = 0;
printf("%d\n", !a && b);
printf("%d\n", a||3+10&&2);第1行输出(0)
第2行输出(1)
解释: 行3 注意运算符优先级,先+,再&&,最后|| -
写出以下程序段的运行结果。
char ch = 'w';
int b = 0;
printf("%d\n", ch || (b = 10));
printf("%d\n", b);第1行输出(1)
第2行输出(0)
解释: 行3 注意||具有短路求值的特性,b=10未执行,因此行4 的输出是0 -
写出满足下列条件的C表达式。
x 和 y 不同时为零。
!(x==0 && y==0)
x!=0 || y!=0
解释: 注意理解题意,不/同时为0
期中考试
- 可以用关系运算符中的“==”运算符,来判断两个数是否相等。( F )
解释:由于浮点数编码的原因,浮点数无法精确地在计算机中被储存,因此==无法判断浮点数之间是否相等,粪题,对策题float f=0.1
printf("%d\n",f==0.1); \\0
printf("%d\n",f!=0.1); \\1 - 设变量定义为 int a[2]={1,3}, *p=&a[0]+1;,则*p的值是( 3 )。
解释: p是指针,是a+1
1121
- 对于以下程序段,则叙述正确的是( D )。 解释: s是字符数组,p是字符指针,不一样;内容不一样,s是字符串,p只是一个地址;长度不等,字符数组包括了’\0’,字符串不包括;*p和s[0]相等,因为s的首地址赋给了p
char s[ ]="china";
char *p;
p = s;
A. s和p完全相同
B. 数组s中的内容和指针变量p中的内容相等
C. 数组s的长度和p所指向的字符串长度相等
D. *p与s[0]相等
1128
- 若程序中有下面的说明和定义,则会发生的情况是( 程序将顺利编译、连接、执行 )。 解释:
struct abc {
int x;
char y;
};
struct abc s1, s2;查了个答案说是编译出错,但是那个答案少了个分号这个没问题,跑过
1226
文件输入输出,由于没有NOIP经历,因此这是一个我个人基本没涉猎过的领域,做个小笔记
文件类型分类与读取模式分类
- 文本文件 - 文本模式
- 二进制文件 - 二进制模式
样例程序
|
键盘,屏幕输入输出也是文件输入输出的一种
- 标准输入:
stdin
- 标准输出:
stdout
fscanf(stdin, “%d”, &x)
与scanf(“%d”, &x)
等价
fprintf(stdout, “%d\n”, x)
与printf(“%d\n”, x)
等价
其他文件读写函数
fgets(s, SIZE, fp)
s为存储的字符数组,SIZE为待输入字符串的大小,fp为文件指针fputs(s, fp)
s为待输出的字符指针,fp为文件指针fseek(fp, nums, ori)
fp为文件指针,nums为偏移字节数,ori为起始位置ftell(fp)
返回fp距离文件开始处的字节数
其他读写模式
模式 | 含义 |
---|---|
r | 只读 |
w | 覆写 |
a | 追加 |
r+ | 更新 |
w+ | 更新并覆写 |
a+ | 更新并追加 |
加个b | 以二进制读写 |