西邮Linux兴趣小组2023纳新题

编程入门 行业动态 更新时间:2024-10-10 10:23:35

西邮Linux<a href=https://www.elefans.com/category/jswz/34/1742277.html style=兴趣小组2023纳新题"/>

西邮Linux兴趣小组2023纳新题

2023纳新

  • 0.鼠鼠我啊,要被祸害了
  • 1.先预测一下~
  • 2.欢迎来到 Linux 兴趣小组
  • 3.一切都翻倍了吗
  • 4.奇怪的输出
  • 5.乍一看就不想看的函数
  • 6.自定义过滤
  • 7.静...态...
  • 8.救命!指针!
  • 9.咋不循环了
  • 11.克隆困境
  • 12.你好,我是内存
  • 13.GNU/Linux (选做)

① 本题目只作为西邮 Linux 兴趣小组 2023 纳新面试的有限参考。
② 为节省版面,本试题的程序源码省去了#include 指令。
③ 本试题中的程序源码仅用于考察 C 语言基础,不应当作为 C 语言「代码风格」的范例。
④ 所有题目编译并运行于 x86_64 GNU/Linux 环境。

0.鼠鼠我啊,要被祸害了

有 1000 瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水,24 小时后就会准时死亡。
至少要多少只小白鼠才能在 24 小时内鉴别出哪瓶水有毒?

10

要找出对应的水,就肯定要给水进行编号,1~1000.
只有一瓶水有毒,要最少的老鼠,那就对如何给老鼠分配这1000瓶水有讲究,我们可以将水的编号转化为2进制.
然后我们将二进制位对应为一的给对应的老鼠喝下去,那么二十四小时候死去的老鼠和为死去的老鼠就将毒药的二进制编号显示出来了.


1.先预测一下~

按照函数要求输入自己的姓名试试~

char *welcome() {// 请你返回自己的姓名
}
int main(void) {char *a = welcome();printf("Hi, 我相信 %s 可以面试成功!\n", a);return 0;
}

看函数的接受值是一个指针,那么我们也需要返回一个地址.
我们可能会想到创建一个数组来存放姓名,这个思路肯定是没问题的,但有两个问题值得考虑:1.姓名的长度大小是多少合适.2.数组出函数内存就会被释放,用动态内存,主函数内没有释放开辟的内存,存在内存泄漏问题.

char *welcome(){
char *name="在这里输入自己的名字";
return name;
}
int main(void) {char *a = welcome();printf("Hi, 我相信 %s 可以面试成功!\n", a);return 0;
}

2.欢迎来到 Linux 兴趣小组

有趣的输出,为什么会这样子呢~

int main(void) {char *ptr0 = "Welcome to Xiyou Linux!";char ptr1[] = "Welcome to Xiyou Linux!";if (*ptr0 == *ptr1) {printf("%d\n", printf("Hello, Linux Group - 2%d", printf("")));}int diff = ptr0 - ptr1;printf("Pointer Difference: %d\n", diff);
}

ptr0与ptr1均为字符串的首元素的地址,对二者进行解引用均得到的为’W’,所以二者相等,满足if条件
此处为printf函数嵌套,用到了printf的返回值,printf返回值为打印元素的个数.并且嵌套从最内侧的开始.
因此先打印Hello, Linux Group - 20.最后打印第二个printf的返回值23.“Hello, Linux Group - 2023
最后打印的值是为了说明,ptr0与ptr1指向的地址是不同的.


3.一切都翻倍了吗

① 请尝试解释一下程序的输出。
② 请谈谈对 sizeof()和 strlen()的理解吧。
③ 什么是 sprintf(),它的参数以及返回值又是什么呢?

int main(void) {char arr[] = {'L', 'i', 'n', 'u', 'x', '\0', '!'}, str[20];short num = 520;int num2 = 1314;printf("%zu\t%zu\t%zu\n", sizeof(*&arr), sizeof(arr + 0),sizeof(num = num2 + 4));printf("%d\n", sprintf(str, "0x%x", num) == num);printf("%zu\t%zu\n", strlen(&str[0] + 1), strlen(arr + 0));
}
7       8       2
0
4       5
  • &arr得到的是整个数组的地址,解引用后拿到了数组,arr有7个char类型,所以为7.arr+0为数组的首元素的地址,所以打印的结果为8.而num是一个short类型,占两个字节,所以打印结果为2.
  • 这里用到了一个字符串函数sprintf,它可以把数据写入字符数组,第一个参数为写入的数组.第二个参数为写入的格式或者内容,后面的参数是对第二个参数的补充.例如本题将num以16进制的形式转换为字符串在与num进行比较.不等结果输出0.
  • 由于short型的num占两个字节,所以以16进制写入只有3位,加上0x只有5位,从数组的第二个元素开始,所以长度为4,arr+0为数组首元素,所以打印的值为5.

sprintf详解


4.奇怪的输出

程序的输出结果是什么?解释一下为什么出现该结果吧~

int main(void) {char a = 64 & 127;char b = 64 ^ 127;char c = -64 >> 6;char ch = a + b - c;printf("a = %d b = %d c = %d\n", a, b, c);printf("ch = %d\n", ch);
}

a = 64 b = 63 c = -1
ch = -128

int main(void) {char a = 64 & 127;// 01000000// 01111111// 01000000//a为01000000   64 char b = 64 ^ 127;// 00111111 //b为00111111   63char c = -64 >> 6;//-64   11000000//      10111111//      11000000//右移后11111111//c为 11111111 所以,打印出来的c为-1char ch = a + b - c;//64+63+1=128,//a+b为01111111由于该处的类型为char类型范围为-128~127,所以加一之后打印出来的为-128,如果想要将打印出来的值变为128,我们可以将char定义为unsigned类型,这样就可以打印出来.printf("a = %d b = %d c = %d\n", a, b, c);//后面的打印均是以%d的形式打印,所以打印的时候会发生整形提升.printf("ch = %d\n", ch);//由于char的范围为-128~127.所以127加一后会变为-128.}

本题主要考察位运算
char的范围详细解释


5.乍一看就不想看的函数

“人们常说互联网凛冬已至,要提高自己的竞争力,可我怎么卷都卷不过别人,只好用一些奇技淫
巧让我的代码变得高深莫测。”
这个 func()函数的功能是什么?是如何实现的?

int func(int a, int b) {if (!a) return b;return func((a & b) << 1, a ^ b);
}
int main(void) {int a = 4, b = 9, c = -7;printf("%d\n", func(a, func(b, c)));
}
6

本题考察到了递归的例题,递归的注意事项,功能是实现实现两个数的相加.
函数内部用到了位运算,同时我们要知道
那么 a ^ b 就是 a + b 相加后,该进位没有进位的运算结果
a & b 就是 a 与 b 二进制对位数字都是1的结果,(a & b) << 1就是进位之后的结果
a+b=(a^b)+((a&b)<<1),直到a<<1==0是停止递归,返回结果.
所以结果为6


6.自定义过滤

请实现 filter()函数:过滤满足条件的数组元素。
提示:使用函数指针作为函数参数并且你需要为新数组分配空间。

typedef int (*Predicate)(int);
int *filter(int *array, int length, Predicate predicate,int *resultLength); /*补全函数*/
int isPositive(int num) { return num > 0; }
int main(void) {int array[] = {-3, -2, -1, 0, 1, 2, 3, 4, 5, 6};int length = sizeof(array) / sizeof(array[0]);int resultLength;int *filteredNumbers = filter(array, length, isPositive,&resultLength);for (int i = 0; i < resultLength; i++) {printf("%d ", filteredNumbers[i]);}printf("\n");free(filteredNumbers);return 0;
}

这里的typedef为重命名,将一个函数指针重命名为Predicate,这样在后面的代码中可以使用该函数.

核心代码如下:

int *filter(int *array, int length, Predicate predicate,int *resultLength) /*补全函数*/
{int j=0;int *filteredNumbers=(int *)malloc(sizeof(int)*length);//该处创建一个新的数组专门用来存放大于0的数for(int i=0;i<length;i++)//遍历原数组的每一位{if(isPositive(array[i]))//判断是否大于0{filteredNumbers[j]=array[i];//将大于0的数字放入新的数组j++;//该变量用来统计和存放新的数组.}}*resultLength=j;//该处将大于0的数组大小返回,以便减少主函数打印的次数.return filteredNumbers;
}int isPositive(int num)//该函数用来筛选大于0的数字,如果大于0就进入if来将大于0的数放入新的数组。{if(num>0){return 1;}return 0;}

7.静…态…

① 如何理解关键字 static?
② static 与变量结合后有什么作用?
③ static 与函数结合后有什么作用?
④ static 与指针结合后有什么作用?
⑤ static 如何影响内存分配?

  • static翻译为静态的,可以理解为将某个变量变为不变的.
  • static与局部变量结合后可以延长局部变量的周期.改变局部变量的存储位置.与全局变量结合后会改变全局变量的链接属性,使得只能在本文件中使用.
  • static与函数结合与与全局变量相同,均改变了链接属性,使得从外部链接属性,改为内部链接属性.
  • 指针也叫指针变量,所以与变量类似.
  • static结合后,将原来在栈区的变量存放近静态区,随着程序的结束而释放.

8.救命!指针!

数组指针是什么?指针数组是什么?函数指针呢?用自己的话说出来更好哦,下面数据类
型的含义都是什么呢?

int (*p)[10];
const int* p[10];
int (*f1(int))(int*, int);
  • 运算的优先级详解
  • int (*p)[10]是一个数组指针,( * )的优先级高,所以是一个指针,指针指向的是一个数组的首元素的地址.
  • conse int *p[10]是一个指针数组,[ ]的优先级高,所以为一个数组,数组的每一个元素都是一个const int *的指针.const只读,不可改变地址指向的内容.
  • int (* f1(int)) (int * ,int)f1是函数名,相当于是一个函数声明,1.函数名为f1,后面的int 为函数f1的参数,返回值为函数指针型,指针指向的函数参数 一个为int *,另一个为int.返回值为int.

9.咋不循环了

程序直接运行,输出的内容是什么意思?

int main(int argc, char* argv[]) {printf("[%d]\n", argc);while (argc) {++argc;}int i = -1, j = argc, k = 1;//j=0;i++ && j++ || k++;//i不为假,继续进行,然后j为假,但在||处继续进行.printf("i = %d, j = %d, k = %d\n", i, j, k);// 0 1 2return EXIT_SUCCESS;

[1]
i = 0, j = 1, k = 2

argc指argument count,即参数计数器,argv指argument vector,即参数数组.程序在运行时传入的第一个参数就是程序的启动路径/文件名,

  • 所以打印的为[ 1 ] .
  • 进入循环之后argc会不断自增直到溢出之后变为0跳出循环.
  • 然后i出为后置++,所以先使用i的值-1,不为假,j为假但后面的为||或运算,所以继续进行.给K在进行++.
  • &&在判断第一个调节为假是直接跳出该处运算,如果为真则继续运算。可以理解为&&是为了找出不成立的条件.
  • ||在判断第一个调节为真时跳出该运算,如果为假则继续运算,与&&刚好相反.

10.到底是不是 TWO

#define CAL(a) a * a * a
#define MAGIC_CAL(a, b) CAL(a) + CAL(b)
int main(void) {int nums = 1;if(16 / CAL(2) == 2) {printf("I'm TWO(ノ>ω<)ノ\n");} else {int nums = MAGIC_CAL(++nums, 2);}printf("%d\n", nums);
}
1

宏只是进行简单的替换.所以程序为.
但为什么结果为1呢,这里还涉及内部运算的nums为新定义在else内的局部变量,在出括号后结束了生命周期,所以打印的是主函数内定义的nums所以结果为1.

{int nums = 1;//if(16 / CAL(2) == 2) if((16/2*2*2)==2){printf("I'm TWO(ノ>ω<)ノ\n");} else{int nums = ++nums * ++nums * ++nums + 2* 2* 2;//MAGIC_CAL(++nums, 2);且这里的nums是仅限于括号内的局部变量,出括号后就释放了内存.}printf("%d\n", nums);
}

11.克隆困境

试着运行一下程序,为什么会出现这样的结果?
直接将 s2 赋值给 s1 会出现哪些问题,应该如何解决?请写出相应代码。

struct Student {char *name;int age;
};
void initializeStudent(struct Student *student, const char *name,int age) {student->name = (char *)malloc(strlen(name) + 1);strcpy(student->name, name);student->age = age;
}
int main(void) {struct Student s1, s2;initializeStudent(&s1, "Tom", 18);initializeStudent(&s2, "Jerry", 28);s1 = s2;printf("s1 的姓名: %s 年龄: %d\n", s1.name, s1.age);printf("s2 的姓名: %s 年龄: %d\n", s2.name, s2.age);free(s1.name);free(s2.name);return 0;
}

s1 的姓名: Jerry 年龄: 28
s2 的姓名: Jerry 年龄: 28

字符串的替换应该使用strcpy函数进行,直接进行赋值有可能会发生语义错误.
直接将s2赋值给s1,会使s1原本指向的字符串tom变为指向jerry,而后面进行释放的时候已经将jerry指向的内存释放,再次释放s2,会重复释放.以及原先的tom字符串的内存没有被释放,造成内存泄漏.
并前将动态内存释放之后应将指针赋值为NULL,否则会形成野指针.

12.你好,我是内存

作为一名合格的 C-Coder,一定对内存很敏感吧~来尝试理解这个程序吧!

struct structure {int foo;//0~3union {//8~23int integer;//联合体大小为16.对齐数为8.char string[11];void *pointer;} node;short bar;//24~25long long baz;//32~39int array[7];//40~67
};//对齐后为0~71
int main(void) {int arr[] = {0x590ff23c, 0x2fbc5a4d, 0x636c6557, 0x20656d6f,//从这里开始打印.0x58206f74, 0x20545055, 0x6577202c, 0x6d6f636c,0x6f742065, 0x79695820, 0x4c20756f, 0x78756e69,0x6f724720, 0x5b207075, 0x33323032, 0x7825 005d,//从这里结束打印. %s遇到'/0'终止.0x636c6557, 0x64fd6d1d};printf("%s\n", ((struct structure *)arr)->node.string);//先将arr强制类型转换为结构体指针,指向了结构体中联合体的char型的数组,所以他开始打印的位置为arr数组的第三块,然后遇到'\0'之后停止打印.
}

Welcome to XUPT , welcome to Xiyou Linux Group [2023]

本题考察涉及强制类型转换,结构体联合体内存对齐,大小端存储模式以及ascll.
ascll考察点与21年13题类似,但这里涉及到了指针指向位置的改变,将arr首元素地址强制类型转化为结构体类型指针,指向结构体内联合体的字符串。所以指针指向的首元素不再是数组刚开始的0x590ff23c.联合体在内存中的对齐位置为8.所以指针向后移动字节,因为arr中为16进制形式,所以两位为为一个字节,指向了0x636c6557.
21年13题。


13.GNU/Linux (选做)

注:嘿!你或许对 Linux 命令不是很熟悉,甚至你没听说过 Linux。但别担心,这是选做题,了解
Linux 是加分项,但不了解也不扣分哦!
你知道 cd 命令的用法与 / . ~ 这些符号的含义吗?

更多推荐

西邮Linux兴趣小组2023纳新题

本文发布于:2024-03-08 22:45:36,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1722697.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:兴趣小组   纳新   西邮   Linux

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!