西邮linux兴趣小组2019-2021三年纳新试题浅析
关于纳新试题,您需要了解:
- 本题仅作为面试有限参考
- 为了代码的简洁,略去了大部分不影响理解的预处理指令
- 题目难度与序号无关
- 若无特殊声明,均假设在 Linux x86_64 GCC 编译器环境下
关于本篇博客,您需要了解:
- 我会假定您已经具备一定的c语言基础,所以部分解析内容会比较简略
- 由于我个人水平有限,若有错误与不足,欢迎您的指出!!!
西邮 Linux 兴趣小组 2019 纳新试题
下面代码段将打印出多少个‘=’?运用相关知识解释该输出。
int main(int argc, char *argv[]) {
for (unsigned int i = 3; i >= 0; i--)
putchar('=');
}
: 将打出多少个 ‘=’ ???(太幽默了))
关于本题,我们只需要了解:
数据名称 | 字节 | 值的范围 |
---|
unsigned int | 4 | 0 到4,294,967,295 |
在减减为零后,由于整数越位,值会变为unsigned int的最大值。
下列三种交换整数的方式是如何实现交换的?
int c = a; a = b ; b = c;
a = a - b; b = b + a; a = b - a;
a ^= b; b ^= a; a ^= b;
对于(1). 通过赋值进行 对于(2). 通过从右向左的赋值运算进行 对于(3). 通过位运算以及复合赋值进行
有如下代码段所示的函数 f,当我们执行该函数时,会产生什么样的输出结果?在同一程序中
多次执行该函数,输出结果是否一致?
void f() {
static int a = 0;
int b = 0;
printf("%d, %d\n", ++a, ++b);
}
关于本题,我们需要了解到静态变量。 在函数f()内: a为一个静态存储期,具有块作用域,无链接变量。 b为一个自动存储期,具有块作用域,无链接变量 a的值会随着函数的不断调用而不断+1,而b的每次输出都为零。
下面程序段的输出是什么?请解释该现象并说出与之相关尽可能多的知识。
int main(void) {
printf("%d\n", printf("Xiyou Linux Group2%d", printf("")));
}
首先!这行代码是对的,这是一个函数嵌套。 printf()的返回值是他成功打印字符的个数 第一个printf()的返回值为19(%d也算一位),第二个printf()的返回值为0。
首先打印Xiyou Linux Group2 然后打印0 最后打印出19 合起来就是Xiyou Linux Group2019
执行下面的代码段,会输出什么?请试着解释其原因,并叙述相关知识。
int main(int argc, char *argv[]) {
char ch = 255;
int d = a + 1;
printf("%d %d", ch, d);
}
此题涉及到原码 反码 补码 的知识
因为a是一个未定义的变量,所以程序无法执行。
但是单看ch的值,我们会打印出-1,为什么呢? char ch的字节为1位,取值范围为-128 – 127,所以ch = 255也是错的, 在用int的强行解释255时,因为255的二进制位为1111 1111,所以它就是255, 但是因为题目中为char类型,所以1111 1111被解释为符号位为1的整数,也就是-1.
执行以下代码段,将产生什么样的输出?请对输出加以解释,并手动计算代码中 t 的值。
int main(int argc, char *argv[]) {
char x = -2, y = 3;
char t = (++x) | (y++);
printf("x = %d, y = %d, t = %d\n", x, y, t);
t = (++x) || (y++);
printf("x = %d, y = %d, t = %d\n", x, y, t);
}
本题输出如下
x = -1, y = 4, t = -1
x = 0, y = 5, t = 1
第一行输出涉及的char范围上题已讲,所以本行着重介绍位运算 我们都了解“||”运算符为“或” 而对于“|”咋可能比较陌生,“|”的含义为位运算中的按位取反(二进制),例如本题: 1111 1111 | 0000 0100 = 1111 1111 即为-1(补码),所以t = -1 (关于位运算的更多用处,例如掩码,打开位,切换位之类运算,可以在《c primer plus》的十五章详细了解)
第二行就是我们熟知的有真则真,在C语言中,非负便为真,所以t = 1
下面代码段的输出结果是什么?输出该结果的原因是?
#define X a+b
int main(int argc, char *argv[]) {
int a = 1, b = 1;
printf("%d\n", X*X);
}
很“简单”的编译预处理,就是有点小套路 一眼看过去答案为4,其实为三,只要明白C语言中运算符优先级即可 a+b*a+b = a+ 1 + b = 3
请解释下面代码段中每一句的效果。
int val = 2018;
int *pi = 2019;
pi = &val;
*pi = 0;
定义一个int类型的变量,为其赋值为2018; 定义一个指向int类型的指针变量,为地址pi为2019(错误); 使指针变量指向 val; 改变val的值为0;
将原题改为:
int val = 2018;
int *pi;
pi = &val;
*pi = 0;
printf("%d",val);
即可输出0;
执行下列程序段,并输入“Xiyou Linux”(不含引号),那么程序的输出结果是什么?请解释其原因。
int main(int argc, char *argv[]) {
char *p = (char *)malloc(sizeof(char) * 20),
*q = p;
scanf("%s %s", p, q);
printf("%s %s\n", p, q);
}
- 首先使用<stdlib.h>库中的malloc()函数分配了一个20字节大小的空间存放char类型并且使p指向它
- 然后声明一个指针变量使得 p,q指向同一块空间
- 输入Xiyou Linux
- 因为Linux是后输入的,且p,q指向的同一空间,所以空间存储的数据被覆盖为Linux
- 所以在输出时将同一空间内容打印两次,即为“Linux Linux”(不含引号)
执行下面的程序段,每次执行的输出结果一致吗,整理并解释输出结果。
int main(int argc, char *argv[]) {
int a[4] = { 2, 0, 1, 9 };
printf("%p, %p\n", a, &a);
printf("%p, %p\n", a + 1, &a + 1);
}
输出如下:
0x7fffffffdb60, 0x7fffffffdb60
0x7fffffffdb64, 0x7fffffffdb70
因为a为动态分配,所以每次执行结果为随机的。
a为首地址,&a为首元素的地址(在数组中首地址即为首元素的地址) a+1为首地址加一个元素大小(4),&a+1为首元素地址加上整个数组的大小(4*4) (db70 - db60 = 16(十六进制))
斐波那契数列是这样的一串数列:1,1,2,3,5,8,13,......。在这串数列中,第一项、第二项
为 1,其他项为前两项之和,该数列的第 x 项可以表示为下面的函数。请根据描述,写出一个程
序,使之输入 x 后,能够输出斐波那契数列的第 x 项(其中 x<30)。当你完成之后,你可以尝试
使用递归解决这个问题。
代码如下:
#include<stdio.h>
int fb(int);
int main(int argc, char **argv)
{
int n;
scanf("%d",&n);
int i = fb(n);
printf("%d\n",i);
}
int fb(int n)
{
int f[31];
f[1] = 1;
f[2] = 1;
for(int i = 3; i<=n; i++){
f[i] = f[i-1] + f[i-2];
}
return f[n];
}
#include<stdio.h>
int fb(int);
int main(int argc, char **argv)
{
int n;
scanf("%d",&n);
int i = fb(n);
printf("%d\n",i);
}
int fb(int n)
{
if(n == 1 || n == 2){
return 1;
}else{
return fb(n-1)+fb(n-2);
}
}
下面代码段是某一种排序算法的简单实现,你知道它是什么吗?请讲解其原理,并尝试改进
它。你还知道哪些排序算法?试着详细描述这些算法。
int main(int argc, char *argv[])
{
int nums[6] = {6, 3, 2, 4, 5, 1};
for (int i = 0; i < 6; i++){
for (int j = i; j < 6; j++){
if (nums[i] > nums[j]){
int c = nums[i];
nums[i] = nums[j];
nums[j] = c;
}
}
}
}
恭喜你已经涉及到数据结构了! 这是一个简单的冒泡排序,如果对其原理有不理解的地方可以使用编译器进行断点调试一步一步执行函数来观察其变化。
请简单叙述两种字节序(大端、小端)的概念,你的机器是什么字节序?试着写一个 C 语言程
序来验证,如果你没有思路,你可以尝试着使用联合体或者指针。
字节顺序是指占内存大于一个字节类型的数据在内存中的存放顺序,通常有大端、小端两种顺序。 小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处; 大端字节序是高字节数据存放在低地址处,低字节数据存放在高地址处.
后面的我也没有思路
[root@xiyoulinux /]# ls -al
total 36
drwxr-xr-x 17 root root 4096 Sep 21 23:45 .
drwxr-xr-x 17 root root 4096 Sep 21 23:45 ..
lrwxrwxrwx 1 root root 7 Aug 21 22:21 bin->usr/bin
drwxr-xr-x 4 root root 2048 Jan 1 1970 boot
drwxr-xr-x 21 root root 3580 Nov 21 21:16 dev
drwxr-xr-x 83 root root 4096 Nov 21 22:12 etc
drwxr-xr-x 4 root root 4096 Sep 22 00:07 home
drwxr-xr-x 2 root root 4096 Aug 21 22:21 mnt
drwxr-x--- 9 root root 4096 Nov 19 19:15 root
dr-xr-xr-x 13 root root 0 Nov 21 21:15 sys
drwxrwxrwt 10 root root 380 Nov 21 22:30 tmp
drwxr-xr-x 9 root root 4096 Nov 21 22:12 usr
在Linux Mint执行如图所示(有删节):
上下两部分大同小异,在每台终端上执行该命令都会有些差别 因为ls -al命令等同于ls -la 查阅tldr手册可知:
- ls: ls命令用于显示文件目录列表,和Windows系统下DOS命令dir类似。
- -a:–all的缩写,显示所有的文件,包括隐藏文件(以.开头的文件)
- -l: 列出长数据串,显示出文件的属性与权限等数据信息
具体: 第一列指文件类型及权限; 第二列指链接占用的节点; 第三列指文件所有者; 第四列指文件所有者的用户组; 第五列指文件大小; 第六列指文件的创建时间或者最近修改时间; 最后一列是文件名称.
完结撒花!!!
西邮 Linux 兴趣小组 2020 纳新试题
(从这里开始如果有相同知识点的题目我会告知题号,不再重复赘述)
运行下面的代码,输出结果是什么,请解释说明。
int i;
int main(int argc, char *argv[])
{
i--;
if (i > sizeof(i)){
printf(">\n");
}else{
printf("<\n");
}
return 0;
}
套路满满!!! 强制类型转换(避免损失精度) 在 i 与 sizeof()的返回值比较时(int 与 unsigned int比较时),int被强制转化为unsigned int,因为全局变量默认为0,所以- -i为“-1”,一被强制转化成了4294967295,所以输出">"。 这题作为第一题真是恶意满满
执行下面的代码段,输出结果和你预想的一样吗,谈谈你对宏的理解
#define A 2 + 2
#define B 3 + 3
#define C A * B
int main(int argc, char *argv[])
{
printf("%d\n", C);
return 0;
}
2019- > 7plus 熟悉的小套路
分析下面程序的输出结果
int main(int argc, char *argv[])
{
char str[] = "Welcome to XiyouLinuxGroup";
printf("%zu %zu\n", strlen(str), sizeof(str));
return 0;
}
<string.h>库中两个常用的函数,了解字符串末尾有‘\0’即可通关本题
在程序中执行此函数,输出结果是什么,在同一程序中多次执行该函数,产生的输出结果是否相同?
void fun()
{
int x = 0;
static int num = 0;
for (int i = 0; i < 5; i++){
x++;
num++;
}
printf("x = %d num = %d\n", x, num);
}
2019 -> 3plus(注意i的定义方法,这是c99标准,i的作用域局限于循环中【块作用域】)
分析以下程序,推测并验证其作用
int main(int argc, char *argv[])
{
int number;
unsigned mask;
mask = 1u << 31;
scanf("%d", &number);
while (mask){
printf("%d", (number & mask) ? 1 : 0);
mask >>= 1;序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处;
}
return 0;
}
是用来处理将十进制的数转化成为二进制
- 1u表示unsigned int 1
- number << n == number *(2的n次方)
- number >> n == number/ (2的n次方)【number非负时】
下面程序的运行结果是什么,请解释说明
int main(int argc, char *argv[])
{
char *str = "Xiyou Linux Group";
printf("%c\n", *str + 1);
return 0;
}
注意*(str+1)与*str+1不同 *(str+1)为首地址加上一个char的大小,输出为i str+1为先取str的值即‘X’,加一后就是‘Y’,输出‘Y’
以下程序段的运行结果是什么,你知道怎么判断两个浮点数是否相同吗?
int main(int argc, char *argv[])
{
double a = 3.14;
float b = a;
if ((float)a == b){
printf("Xiyou");
}
if (a != b){
printf("LinuxGroup\n");
}
return 0;
}
输出结果为XiyouLinuxGroup,老神奇了 float为4字节,double为8字节 a = 3.1400000000000001 float(a) = 3.140000(小数点后六位) b = 3.1400001(小数点后七位) 因为float的精度误差在1e-6,double精度误差在1e-15
所以两个等式全部相等
运行下列的代码,解释运行结果并谈谈自己的理解。
int main(int argc, char *argv[])
{
int a[6] = {0x6f796958, 0x694c2075, 0x2078756e, 0x756f7247, 0x30322070,0};
printf("%d\n", printf("%s", (char *)a));
return 0;
}
输出结果为
Xiyou Linux Group 2020
a的输出加上printf的返回值 a的输出以0x6f796958举例: 将其转换为十进制:两个两个转化为十进制为:111 121 105 88 对应的ASCII码表值为:o y i X 由此可以发现以小端模式为标准的终端可以打印出以上输出
分析下列程序的输出,解释其原因。
int main(int argc, char *argv[])
{
int a[2][3] = {{5, 7}, {5, 2}};
int b[2][3] = {5, 7, 5, 2};
int c[2][2] = {{5, 7}, {5, 2}};
int d[2][2] = {5, 7, 5};
printf("%d %d\n", a[1][1], b[1][1]);
printf("%d %d\n", c[1][1], d[1][1]);
return 0;
}
输出为
2 0
2 0
在初始化数组时未初始化的变量会被默认初始化为零
执行下面的程序段,其输出结果是什么,请根据相关知识,解析其原因。
int main(int argc, char *argv[])
{
int a = 1;
printf("%d\n", *(char *)&a);
}
逐步剖析,首先是(char *),指向char类型的指针,然后对a取地址,再使用解引用符,总结就是a被转换成了char类型,再以%d输出,所以就是1.
下面程序段的输出结果是什么,若取消第三行的const注释,a数组还能被修改吗?如果取消6,8行的注释,程序还能正常运行吗,试着解释其原因
int main(int argc, char *argv[])
{
char a[] = "XiyouLinux\0";
char *b = "XiyouLinux\0";
a[5] = '\0';
printf("%s\n", a);
return 0;
}
a与b分别是字符数组和指向字符串的指针,初始输出为Xiyou 当取消const的注释后,a不再是一个可修改的左值 取消6,8行的注释后程序就不能运行了,因为b为字符串指针,进行b[5] = '\0’是一个未定义操作,可能导致内存访问错误,最好在定义字符串指针时使用以下格式:
const char* p1 = "lin3ux"
12.一个c源文件到一个可执行的系统文件经历了一系列步骤,你了解这个过程吗?谈谈你对gcc的理解
- 首先,我们已经拥有了一个.c源文件,然后经过编译器的处理,会成功生成目标代码.obj,最后在链接器的努力下,它会将库代码,启动代码和目标代码三合一,生成可执行代码!
- gcc:gcc的全称是GNU Compiler Collection,它是一个能够编译多种语言的编译器。
gcc编译程序主要经过四个过程:
预处理 - 编译 - 汇编 - 链接
13 .
解释这个函数的功能,尝试优化它
void sort(int arr[], int size)
{
int i, j, tmp;
for (i = 0; i < size - 1; i++)
{
for (j = 0; j < size - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
同2019 -> 12
(1)使用pwd命令 (2)使用ls -a 命令 (3)
- cd ~/A
- mkdir test
- touch 1.c
(4)
西邮 Linux 兴趣小组 2021 纳新试题
(被这套题折磨了好几天,但同时她也是我进入小组的敲门砖)
- sizeof是一个运算符,求的是数据类型所占空间的大小,即其所占内存空间的字节数(对于字符串包含’\0’);
- strlen是一个函数,遇到’\0\便停止读取,返回字符串的长度。
例如本题,a的值为16,b的值为12
关于本题我们需要理解字节对齐的概念
在gcc编译环境下,对其系数为默认4 有效对齐值是给定值#pragma pack(n)和结构体中最长数据类型长度中较小的那个 在本题中,并未通过#pragma pack(n),(n = 1,2,4,8,16)这一命令来改变内存系数,并且题目中最长的数据类型长度为8,所以本题的有效对齐值便为4。 因此,a与b的值均为4*4,即16
程序如下:
#include<stdio.h>
#include<stdlib.h>
void func(int a[][13], int m, int n);
int main()
{
int arr[10][13];
for(int i = 0;i<10;i++){
for(int j = 0;j<13;j++){
arr[i][j] = rand();
}
}
func(arr,10,13);
return 0;
}
void func(int a[][13], int m, int n)
{
for(int i = 0;i<m;i++){
for(int j = 0;j<n;j++){
printf("%d ",a[i][j]);
}
}
}
传值与传址的区别可以参考2019 -> 3中对定义的解释
#include<stdio.h>
int ver=123;
void func1(int ver)
{
ver++;
printf("ver=%d\n",ver);
}
void func2(int*pr)
{
*pr=1234;
printf("*pr=%d\n",*pr);
pr=5678;
printf("ver=%d\n",ver);
}
int main()
{
int a=0;
int ver=1025;
for(int a=3;a<4;a++)
{
static int a=5;
printf("a=%d\n",a);
a=ver;
printf("a=%d\n",a);
func1(ver);
int ver=7;
printf("ver=%d\n",ver);
func2(&ver);
}
printf("a=%d\tver=%d\n",a,ver);
return 0;
}
注意函数签名为unsigned类型 所以这是一个利用递归函数求和100+99+…+1
这一题再一次涉及到了位运算,分别是
- << 左移(二进制下)
- ~ 按位取反
- | 按位或
- & 按位与
输出结果如下:(ox开头为十六进制,hx为short x,hhx为short short x)
#include<stdio.h>
int main()
{
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
int (*b)[3]=a;
++b;
b[1][1]=10;
int*ptr=(int*)(&a+1);
printf("%d %d %d\n",a[2][1],**(a+1),*(ptr-1));
return 0;
}
const int 和int const都是指该int类型变量不能被修改,没有区别 同理const int和int const也没有区别,都是指指针指向的值不能被改变 关键是看*所在的位置
对于函数们来说
void func2(const int *n)
{
*n+=1;
n=&a;
}
void func3(int*const n)
{
*n+=1;
n=&a;
}
void func4(const int *const n)
{
*n+=1;
n=&a;
}
核心代码如下:
if(copy>='a'&© <= 'z')
copy -= 32;
else if(copy >= 'A'&© <= 'Z')
copy += 32;
new[i++]=copy;
- 1 swap3是错误的,因为块作用域问题
- 2 do…while(0)只循环一次,主要配合#define使用,详细参考:
https://blog.csdn.net/majianfei1023/article/details/45246865 - 3 还可通过指针实现:
void swap(int* p1,int* p2)
{
int temp=*p1;
*p1=*p2;
*p2=temp;
}
- argc为argument count,用来统计程序运行时发送给main函数的命令行参数的个数
- argv为argument value,为字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数
不使用argc,只需要用一个while循环
int main(int argc,char *argv[])
{
int i=0;
printf("argc = %d\n",argc);
while(argv[i] != NUll)
printf("%s\n",argv[i++]);
return 0;
}
func3()有错, n在func1中别定义为静态变量在函数调用结束后不会立即销毁,所以func3不能再对其重新定义
参考2020 -> 8 以小端模式输出以上十六进制数字 输出为:Welcome to xiyou linux group 2021
参考2020 -> 12
程序中的栈属于数据结构的内容,主要是模拟硬件的栈进行数据的存储与处理 一些个人笔记: 堆和栈的区别:栈区内存由编译器自主分配和释放,堆区内存由程序员手动分配和释放。
在调用前声明,然后直接调用即可(extern)
- 文件 touch 文件夹 mkdir
- 对于ls -la来说:
第一列:文件的类型与权限 第二列:有多少文件名连接到此节点(i-node) 第三列:所有者账号 第四列:文件所属用户组 第五列:容量大小,默认单位BYTE 第六列:创建或最近修改日期 第七列:文件名 - 详细参考:https://blog.csdn.net/zyz511919766/article/details/14452027
完结撒花!!!
|