自信15041305759头像
c语言之题目篇复盘封面图

c语言之题目篇复盘

1.

#include<stdio.h>
int main(){
    unsigned char i = 7;
    int j = 0;
    for(; i > 0; i -= 3){
        ++j;
    }
    printf("%d\n", j);
    return 0;
}

问题:该程序的输出是多少?
选项:A 2
B 死循环
C 173
D 172
以下为解答:
—————————————————————————————————————
这里我们应该直到,unsigned char类型为无符号字符类型,取值范围 0 到 255。
所以,当每次-3时,是不可能去减到-2的对吧,由于 i 是无符号类型,当减法结果小于 0 时,会发生模 256 回绕(即结果加上 256,使值落在 0~255 范围内)。大致是如下意思:
在这里插入图片描述
所以,如上图所示,我们在第三次是不会到-2的,而是重新回到了254,也就是说:
在这里插入图片描述
如上所示,
为什么不是死循环?
虽然无符号数减法会回绕,但最终一定会到达 0,因为 3 和 256 互质,循环遍历所有可能值直到 0。若 3 与 256 不互质,则可能进入循环而不遇 0,但此处互质,故有限步后到达 0。

2.

#include <stdio.h>
int main() {
    char a = 0, ch;
    while ((ch = getchar()) != '\n') {
        if (a % 2 != 0 && (ch >= 'a' && ch <= 'z'))
            ch = ch - 'a' + 'A';
        a++;
        putchar(ch);
    }
    printf("\n");
    return 0;
}

问题:若输入 1abcedf2df<回车>,输出结果是?
选项:
A. 1abcdef2df
B. 1ABCDEF2DF
C. 1AbCeDf2dF
D. 1abcDeDF2DF
0%2=0,所以直接a++,a=1,a%2!=0,且ch=a符合if条件,ch=a-‘32’,即为大写A,所以排除ad,接下来,a=2,2%2=0,所以输出为b,所以选c,后面也是这么个逻辑慢慢排即可。

3.

问题:以下哪个选项一定可以将flag的第二个bit置0?
选项:
A. flag &= ~2
B. flag |= 2
C. flag ^= 2
D. flag >>= 2
选项A:flag &= ~2

  • ~2 是对2的按位取反。2的二进制表示为 …00000010,取反后为 …11111101。

  • 与flag进行按位与(&)操作:任何位与1相与保持不变,与0相与则清零。

  • 因此,flag &= ~2 会将bit1清零,其余位保持不变。一定可以将第二个bit置0。

选项B:flag |= 2

  • flag |= 2 是按位或操作,将bit1置为1,无论原来是什么,结果该位都是1。这是置1操作,不是清零。

选项C:flag ^= 2

  • flag ^= 2 是按位异或操作,如果bit1原为0则变为1,原为1则变为0。结果取决于原值,不能保证一定清零。

选项D:flag >>= 2

  • flag >>= 2 是将flag右移2位,这会将所有位向右移动,丢失低两位,并且高位补0(对于无符号数)或补符号位(对于有符号数)。这并非针对特定位的操作,而且会改变其他位。

4.

struct One {
    double d;
    char c;
    int i;
};

struct Two {
    char c;
    double d;
    int i;
};

在 #pragma pack(4) 和 #pragma pack(8) 两种对齐方式下,分别求它们的大小。选项为:

A. 16 24, 16 24

B. 16 20, 16 20

C. 16 16, 16 24

D. 16 16, 24 24
在 #pragma pack(4)情况下:
在这里插入图片描述

在#pragma pack(8) 情况下:
在这里插入图片描述
文字说明:
逐步计算
结构体 One
情况一:#pragma pack(4)
double d:自身对齐8,实际对齐 min(8,4)=4,占用8字节。

char c:自身对齐1,实际对齐1,占用1字节。

int i:自身对齐4,实际对齐4,占用4字节。

内存布局:

d 从偏移0开始,占 [0,7]。

c 紧随其后,偏移8,占 [8]。

i 需要4字节对齐,当前偏移9,填充到12,占 [12,15]。

总大小 = 16字节,是4的倍数,满足整体对齐。

大小 = 16

情况二:#pragma pack(8)
double d:实际对齐 min(8,8)=8,占8字节。

char c:实际对齐1,占1字节。

int i:实际对齐4,占4字节。

内存布局:

d 占 [0,7]。

c 占 [8]。

i 需4字节对齐,当前偏移9,填充到12,占 [12,15]。

总大小16,是8的倍数(16是8的倍数),无需填充。

大小 = 16

因此,One 在两种pack下均为16。

结构体 Two
情况一:#pragma pack(4)
char c:实际对齐1,占1字节。

double d:实际对齐 min(8,4)=4,占8字节。

int i:实际对齐4,占4字节。

内存布局:

c 占 [0]。

d 需要4字节对齐,当前偏移1,填充到4,占 [4,11]。

i 需要4字节对齐,当前偏移12,正好是4的倍数,占 [12,15]。

总大小16,是4的倍数。

大小 = 16

情况二:#pragma pack(8)
char c:实际对齐1,占1字节。

double d:实际对齐 min(8,8)=8,占8字节。

int i:实际对齐4,占4字节。

内存布局:

c 占 [0]。

d 需要8字节对齐,当前偏移1,填充到8,占 [8,15]。

i 需要4字节对齐,当前偏移16,是4的倍数,占 [16,19]。

此时总大小为20字节。但结构体整体对齐要求为最大成员实际对齐值,即8。20不是8的倍数,因此需要在末尾填充4字节,使大小达到24(20+4)。

大小 = 24

因此,Two 在pack=4下为16,pack=8下为24。

5.

int x = 1;
do{
    printf("%2d\n",x++);
}while(x--);

问题:程序的输出结果是?
选项:A 1
B 无任何输出
C 2
D 陷入死循环
这个其实很好理解,比如说先打印1,x++为2,之后x又–为1,和开始状态一样,从此,进入死循环状态,答案为D。

6.

#include<stdio.h>
#include<stdlib.h>
void main()
{
    int a = -3;
    unsigned int b = 2;
    long c = a + b;
    printf("%ld\n", c);
}

问题:在32位环境下,程序输出结果是什么?
选项:
A. -1
B. 4294967295
C. 0x7FFFFFFF
D. 0xFFFFFFFF
我们先来看一下-3的原码:1000 0000 0000 0000 0000 0000 0000 0011,我们进行取反+1的一个操作得到补码为:1111 1111 1111 1111 1111 1111 1111 1101,2为正数,原码即为补码,即:
0000 0000 0000 0000 0000 0000 0000 0010,我们进行相加即:1111 1111 1111 1111 1111 1111 1111 1111此为-1的补码,如果看不出来,那么再进行一次取反+1,即:1000 0000 0000 0000 0000 0000 0000 0001,此为-1.
为什么不是其他选项?

  • B. 4294967295:这是无符号数,但 c 是有符号 long,输出时会按有符号解释。

  • C. 0x7FFFFFFF:这是最大正有符号数,与计算结果不符。

  • D. 0xFFFFFFFF:这是十六进制表示,但 %ld 输出十进制,且结果应为 -1。

7.

char *p[] = {"Shanghai", "Beijing", "Honkong"};

问题:以下哪个表达式的结果是一个字符?
选项:
A. *p[1] + 3
B. *(p[1] + 3)
C. *(p[3] + 1)
D. p[3][1]

数据结构理解:

  • p 是一个指针数组,包含3个元素,每个元素是指向字符串的指针。

  • p[0] 指向字符串 “Shanghai” 的首字符 ‘S’。

  • p[1] 指向字符串 “Beijing” 的首字符 ‘B’。

  • p[2] 指向字符串 “Honkong” 的首字符 ‘H’。

  • 数组下标从0开始,因此 p[3] 是越界访问,属于未定义行为。

在这里插入图片描述
对于A选项,p[1]为指向字符 ‘B’。*p[1] 取出该字符,值为 'B’然后 + 3 进行整数加法,得到 66 + 3 = 69,对应字符 ‘E’。即A错误
对于B选项,同上,指向B后,+3,找到指向j,解引用就为j,所以B正确,
对于CD选项,不再赘述,跟生面都是相同的道理

8.

int f(int x){
    return ((x>2) ? x*f(x-1) : 3);
}
int i;
i = f(f(2));

问题:执行后 i 的值为?
选项:A 30
B 无限递归
C 9
D 2160
此题很简单,简单的数学题,把值代入进去即可,在此不再赘述,正确答案为C

9.

int p[][4] = {{1}, {3, 2}, {4, 5, 6}, {0}};

问题:p[1][2] 的值是?
选项:A 1
B 0
C 6
D 2
此题很简单,把图画出来即可,如下:
在这里插入图片描述
所以选C

10.

int fun(int a){
    a ^= (1 << 5) - 1;
    return a;
}
fun(21);

问题:函数调用 fun(21) 的返回值是多少?
选项:A 10
B 5
C 3
D 8
首先,这道题我们要搞懂的是(1 << 5) - 1 这个是什么意思?
1 << 5:即将1左移5位,二进制位即为100000,-1,即为011111.
语句 a ^= (1 << 5) - 1
等价于 a = a ^ 31。按位异或运算,将 a 的低5位取反,高位保持不变。a=21(16+4+1),二进制位即为10101,异或运算:

  10101
^ 11111
= 01010

结果即为8+2等于10,所以选A

11.

问题:下列关于C/C++的宏定义,不正确的是哪个?
选项:
A. 宏定义不检查参数正确性,会有安全隐患
B. 宏定义的常量更容易理解,如果可以使用宏定义常量的话,要避免使用const常量
C. 宏的嵌套定义过多会影响程序的可读性,而且很容易出错
D. 相对于函数调用,宏定义可以提高程序的运行效率
逐项分析
选项A(正确)

  • 宏定义是简单的文本替换,不进行类型检查,参数的任何表达式都可能被直接替换,容易引发意料之外的错误(如优先级问题、副作用重复计算等),确实存在安全隐患。

选项B(错误)

  • 在C/C++中,使用 const 常量比宏定义常量更优越,因为 const 有类型检查、作用域规则,且便于调试。宏定义只是文本替换,缺乏类型信息,容易出错。因此,应优先使用 const 而非宏定义常量,选项B的说法恰恰相反,是不正确的。

选项C(正确)

  • 宏嵌套过多会导致代码难以阅读和维护,且由于宏展开的复杂性,很容易引入错误。

选项D(正确)

  • 宏展开是预处理阶段完成的,避免了函数调用的开销(如压栈、跳转等),在某些情况下可以提高运行效率。但需注意,滥用宏也可能导致代码膨胀和副作用问题。

12.

问题:下面关于“指针”的描述不正确的是?
选项:
A. 当使用 free 释放掉一个指针内容后,指针变量的值被置为 NULL
B. 32位系统下任何类型指针的长度都是4个字节
C. 指针的数据类型声明的是指针实际指向内容的数据类型
D. 野指针是指尚未分配或者已经释放的内存地址
选项A(不正确)

  • free 函数用于释放动态分配的内存,但不会自动将指针变量置为 NULL。释放后,指针仍然指向原来的地址(成为悬空指针),继续使用该指针会导致未定义行为。正确的做法是手动将指针赋值为 NULL。

选项B(正确)

  • 在32位系统中,内存地址宽度为32位,因此任何类型的指针都占用4个字节。

选项C(正确)

  • 指针的类型声明决定了指针所指向数据的类型,例如 int* 指向整型数据,编译器据此进行指针运算和类型检查。

选项D(正确)

  • 野指针通常指指向无效内存的指针,包括未初始化、已释放或越界的地址,描述基本正确。

13.

问题:由多个源文件组成的C程序,经过编辑、预处理、编译、链接等阶段会生成最终的可执行程序。下面哪个阶段可以发现被调用的函数未定义?
选项:A. 预处理
B. 编译
C. 链接
D. 执行
各阶段作用

  • 预处理:处理宏定义、头文件包含、条件编译等,不涉及函数定义的检查。

  • 编译:将预处理后的源文件编译成目标文件(.obj或.o)。编译器会检查语法和类型,但函数调用只需有声明即可,定义可以存在于其他源文件中,因此编译阶段不会发现未定义的函数。

  • 链接:将多个目标文件和库合并成可执行文件。此时,链接器会查找所有被调用的函数的定义,如果找不到,就会报“未定义引用”错误。

  • 执行:程序运行阶段,此时如果函数未定义,程序根本无法生成可执行文件,因此不会到执行阶段。

14.

#define N 3+1
#define Y(n) ((N+1)*n)
z = 2 * (N + Y(5+1));

问题:执行后 z 的值为?
选项:A 60
B 190
C 248
D 上述答案都不对
此题其实也是纯纯的数学替换值的游戏,再次不再赘述,正确答案为A

15.

char a;
int b;
float c;
double d;

问题:表达式 a * b + d - c 的值的类型是?
选项:A. float
B. int
C. char
D. double
此题是关于整型提升方面的题目,C语言中,当不同类型的数据进行混合运算时,会按照隐式类型转换规则(通常称为“算术转换”)将操作数转换为同一类型,然后再计算。转换规则遵循提升顺序,一般向精度更高、范围更大的类型转换。比说:
char和int相乘,那么int范围更大,所以整型提升为int,接下来同理,double范围更大,所以最后整型提升为double

16.

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()

{
	int i = 0;
	int n = 0;//123 456 7
	scanf("%d", &n);
	int k = 0;
	char arr[14] = { 0 };
	while (n)
	{
		if (k % 3 == 0 && k != 0)
		{
			arr[i] = ',';
			i++;
		}
		arr[i] = n % 10 + '0';
		i++;
		k++;
		n /= 10;
	}
	while (i >= 0)
	{
		printf("%c", arr[i]);
		i--;
	}
	return 0;
}

这个逻辑较为简单,用到%10,/10循环即可,逻辑:
我可以让arr数组倒着存入,用k作为标记,每到3次,那么就添个逗号,最后倒着输出即可。

17.

在这里插入图片描述

int exist(char ch, char arr2[])
{
	int j = 0;
	while (arr2[j])
	{
		if (ch == arr2[j])
		{
			return 1;
		}
		j++;
	}
	return 0;
}

int main()
{
	char arr1[101] = { 0 };
	char arr2[101] = { 0 };

	gets(arr1);
	gets(arr2);
	
	int i = 0;
	while (arr1[i])
	{
		if (exist(arr1[i], arr2) == 0)
		{
			printf("%c", arr1[i]);
		}
		i++;
	}
	return 0;
}

这道题其实解法有很多种,但是复杂程度不同,比如我可以取第一行的每个字符和第二个字符串去对比,如果第二个字符串里没有,那么就保留哪个字符。
我们这里是手动写了一个函数,实际上,我们可以使用库函数strchr来解决问题,如下:
在这里插入图片描述

int main()
{
	char arr1[101] = { 0 };
	char arr2[101] = { 0 };

	gets(arr1);
	gets(arr2);

	int i = 0;
	while (arr1[i])
	{
		if (strchr(arr2,arr1[i])==NULL)
		{
			printf("%c", arr1[i]);
		}
		i++;
	}
	return 0;
}

当然,要包含string.h这个头文件才能使用,最后效果也是和前一个相同的。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/cxw15041305759/article/details/158618107

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--