2022面试题题解(2/3)
0. 我的计算器坏了?!
2^10=1024
对应于十进制的4位,那么2^10000
对应于十进制的多少位呢?
1.Solution
结果:3011
- 10m<x<10(m+1)
- m<log10x<(m+1)
- m=⌊log10x⌋+1
#include<stdio.h>
#include <math.h>
int main()
{
int y;
scanf("%d",&y);
int m;
m=y*log10(2)+1;
printf("%d",m);
return 0;
}
1. printf还能这么玩?
尝试着解释程序的输出。
int main(void) {
if ((3 + 2 < 2) > (3 + 2 > 2))
printf("Welcome to Xiyou Linux Group\n");
else
printf("%d\n", printf("Xiyou Linux Group - 2%d", printf("")));
}
- 1.Solution
- 输出结果为:Xiyou Linux Group - 2022
- 这道题是对于printf的返回值与输出顺序的考察:
- ①返回值:若成功则返回输出的字符数,输出出错则返回负值
- ②输出顺序:printf函数从左往右读取,然后将先读取放到栈底,最后读取的放在栈顶,处理时候是从栈顶开始的.所我们看见的结果是,从右边开始处理的.
- -
2. 你好你好你好呀!
- 程序的输出有点奇怪,请尝试解释一下程序的输出吧。
- 请谈谈对
sizeof()
及strlen()
的理解吧。
int main(void)
{
char p0[] = "Hello,Linux";
char *p1 = "Hello,Linux";
char p2[11] = "Hello,Linux";
printf("p0==p1: %d, strcmp(p0,p2): %d\n", p0 == p1, strcmp(p0, p2));
printf("sizeof(p0): %zu, sizeof(p1): %zu, sizeof(*p2): %zu \n",
sizeof(p0), sizeof(p1), sizeof(*p2));
printf("strlen(p0): %zu, strlen(p1): %zu\n", strlen(p0), strlen(p1));
}
- 2.Solution
- 运行结果:p0==p1: 0, strcmp(p0,p2): -72
- sizeof(p0): 12, sizeof(p1): 8, sizeof(*p2): 1
- strlen(p0): 11, strlen(p1): 11
- 这道题的主要思想是两种字符串的表述方式:
- ①一种是指针表示法,一种是数组名表示法
- ②对sizeof与strlen的理解
首先是数据存储位置的概念:
- 栈区 (stack) : 存放内容 : 局部变量, 参数;
- 堆区 (heap) : 存放内容 : 存放程序运行中 动态分配 内存的数据;
- 全局区/静态区 : 存放内容 : 全局变量, 静态变量;
- 常量区 : 存放内容 : 常量; (比如char *s = “hello”,此处的hello就存储在常量区)
- 代码区 (text segment) : 存放内容 : 存放函数体的二进制代码。
再来看看指针与数组的区别:
- p1是一个存放在栈区的字符串指针,而它的值,是一个地址,该地址指向存放在常量区的字符串;
- p0是一个存放在栈区的字符串数组,而它的值,就是字符串本身。
strcmp函数
- strcmp接受字符串地址,依次比较所含字符的ASCii编码
- 上述的差别在于\0的机制:
- p0的大小由字符常量所决定,故最后一位可自动补\0
- p2的大小已经确定,无位置补\0,
- 所以p0在\0的位置时,p2对应的是该数组之后的乱码
sizeof与strlen函数
- 共同点:都是以字节为单位返回大小
- 区别 :strlen是函数,而sizeof是算符
- strlen:返回字符串的大小,所以以\0为截止
- sizeof:返回对象所占内存的大小
- -
3. 换个变量名不行吗?
请结合本题,分别谈谈你对C语言中「全局变量」和「局部变量」的「生命周期」理解。
int a = 3;
void test()
{
int a = 1;
a += 1;
{
int a = a + 1;
printf("a1= %d\n", a);
}
printf("a2= %d\n", a);
}
int main(void)
{
test();
printf("a3= %d\n", a);
}
3.Solution
- 运行结果:a1= 1,a2= 2,a3= 3
- ①全局变量 :
- 存储位置 : 存放在静态存储区中。因此他们的生存周期是固定的,存在于程序的整个运行过程中。
- 全局变量的作用范围 : 一般是从定义位置开始到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。
- ②局部变量 :
- 局部变量的有效范围是局限于函数内部的,形参也是局部变量局部变量的改变无法影响到主调函数
-生命周期 :
- ①把局部变量称为自动变量,即函数被调用时,系统自动为其局部变量分配存储单元;一旦该函数调用结束(不一定是整个程序运行结束),所有分配给局部变量的单元由系统自动回收
- ②动态存储区是使用堆栈来管理的,适合函数动态分配与回收存储单元。而静态存储区相对固定,它用于存放全局变量和静态变量
- 自动变量若没有赋初值,那么其存储单元是随机值。而静态局部变量如果定义时没有赋初值,系统会自动赋0
- -
4. 内存对不齐
union
与struct
各有什么特点呢,你了解他们的内存分配模式吗。
typedef union
{
long l;
int i[5];
char c;
} UNION;
typedef struct
{
int like;
UNION coin;
double collect;
} STRUCT;
int main(void)
{
printf("sizeof (UNION) = %zu \n", sizeof(UNION));
printf("sizeof (STRUCT) = %zu \n", sizeof(STRUCT));
}
4.Solution
- 运行结果:sizeof (UNION) = 24 , sizeof (STRUCT) = 40
- 共同点:都涉及到内存的对齐–>既要能够储存所有的数值,又要是各个数据类型的整数倍
- 不同点:
- 在union中,所有的联合成员共用一个空间,并且同一时间只能储存其中一个成员变量的值。
- 在struct中,地址必须是从这个数据类型大小的整数倍开始,且内存大小为每个数据内存的加和。
- ┑( ̄。。 ̄)┍
5. Bitwise
- 请使用纸笔推导出程序的输出结果。
- 请谈谈你对位运算的理解。
int main(void)
{
unsigned char a = 4 | 7;
a <<= 3;
unsigned char b = 5 & 7;
b >>= 3;
unsigned char c = 6 ^ 7;
c = ~c;
unsigned short d = (a ^ c) << 3;
signed char e = -63;
e <<= 2;
printf("a: %d, b: %d, c: %d, d: %d \n", a, b, c, (char)d);
printf("e: %#x \n", e);
}
5.Solution
- 运行结果:
a: 56, b: 0, c: 254, d: 48
e: 0x4
符号 | 描述 | 运算规则 | 实例 |
---|---|---|---|
& | 与 | 两个都为1,结果为1 | 0001 & 0001 = 1 |
| | 或 | 两个位都为0时,结果才为0。 | 0000|0001=0001 |
^ | 异或 | 两个位相同为0,相异为1 | 0001 ∧ 0001 = 0000 |
~ | 取反 | 0变1,1变0。 | ∼ 0 = 1 , ∼ 1 = 0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0。 | 0001 < < k = 0100 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,右移补1 11。 | 0100 > > k = 0001 |
6. 英译汉
请说说下面数据类型的含义,谈谈
const
的作用。
char *const p
。char const *p
。const char *p
。
6.Solution
- 第一个:它不能在指向别的变量,但指向(变量)的值可以修改。
- 第二个:它不能在指向别的变量,但指向(变量)的值可以修改。
- 第三个:它指向的值不能修改
- ┑( ̄Д  ̄)┍
7. 汉译英
请用变量
p
给出下面的定义:
- 含有10个指向
int
的指针的数组。- 指向含有10个
int
数组的指针。- 含有3个「指向函数的指针」的数组,被指向的函数有1个
int
参数并返回int
。
7.Solution
- 1.int num[10];
- 2.int (*num)[10];
- 3.int (*num[3])(int );
- -
8. 混乱中建立秩序
你对排序算法了解多少呢?
请谈谈你所了解的排序算法的思想、稳定性、时间复杂度、空间复杂度。提示:动动你的小手敲出来更好哦~
8.Solution
排序算法
- 需要使用额外空间的排序:桶排序,归并排序
- 平均情况为O(n^2):冒泡,简单插入,直接插入,
- 平均情况为O(nlogn):归并,快排
- 平均情况为0(nlogn~n^2):希尔
- 思想
- 本质上一般利用两个for循环进行逐一交接,交换
- 优化:因为排序本质是对逆序数的改变,所以这是一个数学问题,通过分块操作,使前面的块普遍较小,后面的块普遍较大,这样可以在平均情况下突破O(n^2)的限制
- 稳定性:
- 数列中有相同的元素时,排序之后两个的顺序不变
- 空间复杂度:
- 运行时所额外占的空间体积
- ┑( ̄▽  ̄)┍
9. 手脑并用
请实现ConvertAndMerge函数:
拼接输入的两个字符串,并翻转拼接后得到的新字符串中所有字母的大小写。提示:你需要为新字符串分配空间。
char* convertAndMerge(/*补全签名*/);
int main(void) {
char words[2][20] = {
"Welcome to Xiyou ", "Linux Group 2022"};
printf("%s\n", words[0]);
printf("%s\n", words[1]);
char *str = convertAndMerge(words);
printf("str = %s\n", str);
free(str);
}
9.Solution
#include <Stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *converAndMerge(char (*words)[20]);
int main()
{
char words[2][20]={
"welcome to xi you ","linux group 2022"};
// printf("%s\n",words[0]);
// printf("%s\n",words[1]);
char *str=converAndMerge(words);
printf("str=%s\n",str);
free(str);
}
**************************************************
converandmerge的代码如下:
char *converAndMerge(char (*words)[20])
{
char *NEW;
char *OK;
NEW=(char *)malloc(40);
NEW=strcat(words[0],words[1]);
OK=NEW;
while(*NEW)
{
if(islower(*NEW))
*NEW=toupper(*NEW);
else if(isupper(*NEW))
*NEW=tolower(*NEW);
NEW++;
}
return OK;
}
10. 给你我的指针,访问我的心声
程序的输出有点奇怪,请尝试解释一下程序的输出吧。
int main(int argc, char **argv) {
int arr[5][5];
int a = 0;
for (int i = 0; i < 5; i++) {
int *temp = *(arr + i);
for (; temp < arr[5]; temp++) *temp = a++;
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr[i][j]);
}
}
}
10.Solution
运行结果:
- 0 1 2 3 4 25 26 27 28 29 45 46 47 48 49 60 61 62 63 64 70 71 72 73 74
- 分析:
- 这道题主要是二维数组的顺序问题:
- arr[0],arr[1],arr[2]……都存储着相对应数组的地址
数组名 | 对应地址 |
---|---|
arr[0] | &arr[0][0] |
arr[1] | &arr[1][0] |
…… | …… |
arr[4] | &arr[4][0] |
其次的有关于指针与数组名的运算
- 进行+,-的运算,每次移动所指向对象所占的字节乘以+与-的大小的距离
- 指针可以进行单目++,–运算
- 数组名不可以进行单目运算,否则首地址位置一变,所指向对象就变了
- …
11. 奇怪的参数
你了解argc和argv吗?
直接运行程序argc的值为什么是1?
程序会出现死循环吗?
#include <stdio.h>
int main(int argc, char **argv)
{
printf("argc = %d\n", argc);
while (1)
{
argc++;
if (argc < 0)
{
printf("%s\n", (char *)argv[0]);
break;
}
}
}
11.Solution
- 运行结果:argc = 1
- /home/XYT公共的/code/no-name
- argc : 命令行传入参数的总个数
- argv : *argv[]是一个指针数组,里面存放的指针指向所有的命令行参数,argv[0]指向程序的全局路径,argv[1]指向在命令行中执行程序名后的第一个字符串,argv[2]指向第二个。
- 可执行完,argc为int型,范围为-231~231
- ヽ(ˋ▽ˊ)ノ
12. 奇怪的字符
程序的输出有点奇怪,请尝试解释一下程序的输出吧。
int main(int argc, char **argv)
{
int data1[2][3] ={
{
0x636c6557, 0x20656d6f, 0x58206f74}
{
0x756f7969, 0x6e694c20, 0x00000000}};
int data2[] = {
0x47207875, 0x70756f72, 0x32303220, 0x00000a32};
char *a = (char *)data1;
char *b = (char *)data2;
char buf[1024];
strcpy(buf, a);
strcat(buf, b);
printf("%s \n", buf);
if(*buf='W') printf("LE");
else printf("BE");
}
12.Solution
运行结果:
Welcome to Xiyou Linux Group 2022
LE
对于一段16进制的解析:
- 对于一个,如(0X636c6557)16进制数,在计算机中以二进制来储存:0110 0011 0110 1100 0110 0101 0101 0111
二进制代码 | ASCII编码 | 字符 |
---|---|---|
01100011 | 99 | c |
01101100 | 108 | l |
01101010 | 101 | e |
01010111 | 87 | W |
- 其次在计算机中对于读取一段数据采用小端序读取
- 就是从后往前的读取
- -
13. 小试宏刀
- 请谈谈你对
#define
的理解。- 请尝试着解释程序的输出。
#include <stdio.h>
#define SWAP(a, b, t) t = a; a = b; b = t
#define SQUARE(a) a *a
#define SWAPWHEN(a, b, t, cond) if (cond) SWAP(a, b, t)
int main() {
int tmp;
int x = 1;
int y = 2;
int z = 3;
int w = 3;
SWAP(x, y, tmp);
printf("x = %d, y = %d, tmp = %d\n", x, y, tmp);
if (x > y) SWAP(x, y, tmp);
printf("x = %d, y = %d, tmp = %d\n", x, y, tmp);
SWAPWHEN(x, y, tmp, SQUARE(1 + 2 + z++ + ++w) == 100);
printf("x = %d, y = %d,tmp=%d\n", x, y, tmp);
printf("z = %d, w = %d ,tmp = %d\n", z, w, tmp);
}
13.Solution
- 运行结果:
- x = 2, y = 1, tmp = 1
- x = 1, y = 2, tmp = 2
- x = 2, y = 2,tmp=2
- z = 5, w = 5 ,tmp = 2
分析:
- 这道题主要时考察define替换时的括号问题
- ①SQUARE(a) a *a中应该为 (a)乘(a);
- ②SWAP(a, b, t) t = a; a = b; b = t也应该有大括号,否则SWAP只包含t=a这一个语句
- ③SWAP的未定义的行为,将SWAP的表达式展开后,有两个z++与w++,而程序对于先执行那个的结果是未定义的,在c语言中编译器可以自行选择优先对函数中哪个参数求值,这样加快了编译器的效率,详见cpp(p102)
- -
14. GNU/Linux命令
你知道以下命令的含义和用法吗:
ls
rm
whoami