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




