2022西邮linux兴趣小组纳新题题解

0. 我的计算器坏了?!

2^10=1024对应于十进制的4位,那么2^10000对应于十进制的多少位呢?

  • 对于这题,可以运用数学的方法进行计算
    第n位的位数=n*log2+1(log2≈0.30103)
    将n=10000代入,得:3010.3+1=3011(取整)

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("")));
}
  • 由if语句的判断易得程序进入else的printf,此时涉及到一个知识点:printf的返回值
  • 返回值是指printf输出的字符数量(包括数字,字母,标点符号,空格等)
  • 现在再看题目:
    1,由于是printf的嵌套,所以从左向右执行,第一个printf要调用第二个printf,第二个printf要调用第三个printf;
    2,第三个printf没有输出,返回值为0,返回给第二个printf
    3,第二个printf输出Xiyou Linux Group - 20,返回值为22,返回给第一个printf
    4,第一个printf输出22
  • 因此,程序最后的输出为Xiyou Linux Group - 2022

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));
}
  • 对于第一个printf,由于p0和p1的地址不同,所以p0 == p1的结果为0;而p0和p2的字符串完全相同,由strcmp函数(比较字符串中对应位置上的字符的大小)知,strcmp(p0, p2)的结果为0
  • 第二个printf,先解释 %zu ,它和%d的区别在于:%d输出int型,%zu输出size_t型,而size_t在库中定义为unsigned int,即:一个是整型,一个是无符号整型
    – 现在再回头来看,sizeof(p0)计算的是字符串的长度加上末尾的**\0**,因此输出为12sizeof(p1)计算的是p1即指针的大小,在32位上结果为464位上结果为8;因p2是char类型(char数组相当于char),所以*p2是char类型,sizeof(*p2)结果是char的大小,即为1
  • 第三个printf,strlen(p0)strlen(p1)结果仅仅是字符串的长度,不包括结尾的\0,因此均输出11

3. 换个变量名不行吗?

请结合本题,分别谈谈你对C语言中「全局变量」和「局部变量」的「生命周期」理解。

int a = 3;
void test()
{
    int a = 1;
    a += 1;
    {
        int a = a + 1;
        printf("a = %d\n", a);
    }
    printf("a = %d\n", a);
}
int main(void)
{
    test();
    printf("a= %d\n", a);
}
  • 二话不说,先出运行结果 27(随机值) 2 3
  • 解释:将一对大括号称为“块”
    1.本地变量定义在块内,且不会被默认初始化(因此第一个的输出结果为随机值)
    2.程序运行到“块”之前,其中的变量不存在;离开“块”,其中的变量消失了
    3.“块”外面定义的变量其一下到处有效
    4.“块”里面定义了和外面同名的变量,则会掩盖外面的

4. 内存对不齐

unionstruct各有什么特点呢,你了解他们的内存分配模式吗。

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));
}
  • 上结果:20 32

  • union(联合体)内部的成员共享一段内存,因此联合体的大小满足:
    ①大小足够容纳最宽的成员(一般取决于其内部最大成员类型的长度*)
    ②大小能被其包含的所有基本数据类型的大小所整除
    *注:以下例子并不满足①中的一般条件,而是要用到第②点
    在这里插入图片描述
    结果为16,原因在于int数组大小的12并不能被大小为8的double所整除
    将b[3]换成b[5],则结果为24(24%4=0;24%8=0)

  • struct(结构体)
    ①结构体内部的成员不是连续紧挨着存放的
    ②结构体可以分成N份,每份的大小是最大成员类型的长度
    ③结构体的大小为N * 最大成员类型的长度

  • 关于32的解释见下:在这里插入图片描述

  • 更多关于内存对齐原则:click here

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);
}
  • 这里涉及到了位运算的知识点,纸笔推导过程省略,但以下给出其运行结果:
    a:56 b:0 c:254 d:48 e:0x4
  • 理解:1.计算机内部的数在内存中以二进制形式储存
    2.位运算就是直接对整数在内存中的二进制数进行操作
    3.以二进制补码运算
    4.优点:节约内存,效率高
    emmm…想多了解关于位运算的知识:戳这里就对啦

6. 英译汉

请说说下面数据类型的含义,谈谈const的作用。

  1. char *const p
  2. char const *p
  3. const char *p
  • 第一个:相当于char p[],p的地址不能变,但*p的值可以改变
  • 第二、三个:它们等价,均声明了一个指向字符常量的指针(因此不能改变它所指的字符),*p的值不能变p的地址可以改变
  • const的作用
    1.给读代码的人这个参数的应用目的
    2.使函数能处理const和非const的实参
    3.能产生更加紧凑的代码
    4.保护被修饰的东西
    这,,,没看够? >> 还是戳这里啦

7. 汉译英

请用变量p给出下面的定义:

  1. 含有10个指向int的指针的数组。
  2. 指向含有10个int数组的指针。
  3. 含有3个「指向函数的指针」的数组,被指向的函数有1个int参数并返回int
  • 第一个:int*p[10] p和[10]结合,构成一个名为p的数组,int*修饰数组的内容(数组的每个元素),所以p数组中有10个指向int类型的指针
  • 第二个:int(*p)[10]p和*结合,构成一个名为p指针,int修饰数组的内容(数组的每个元素),因此p指针指向该数组的首地址,而这个数组有10个单元,每个单元都是int类型,且该数组没有名字
  • 第三个:int(*p[3])(int) (int)表示函数的参数,表明被指向的函数有一个int的参数,最前面的int表明返回类型是int

  • 这里有个知识点:优先级问题
  • 由于优先级()> [ ] > * ,所以要在合适的地方加上()从而达到目的

8. 混乱中建立秩序

你对排序算法了解多少呢?
请谈谈你所了解的排序算法的思想、稳定性、时间复杂度、空间复杂度。

  1. 冒泡排序
void BubbleSort(int a[], int n)  //n为数组长度 
{
    int i, j, t;
    for(i=0; i<n-1; i++)
        for(j=0; j<n-1-i; j++)
        {
            if(a[j]>a[j+1])
            {
                t=a[j];
                a[j]=a[j+1];
                a[j+1]=t;
            }
        }
}

2.选择排序

void selectSort(int a[], int n)
{
    int i, j ,min ,t;
    for( i=0; i<n-1; i++)
    {
        min=i;   //每趟排序最小值先等于这一轮的第一个数,遍历剩下的数
        for( j=i+1; j<n; j++)  //从i下一个数开始检查
        {
            if(a[min]>a[j])
            {
                min=j;
            }
        }
        if(min!=i)
        {
            t=a[min];
            a[min]=a[i];
            a[i]=t;
        }  //结束后开始第二轮 
    }
} 

3.插入排序

void insertSort(int a[], int n)
{
    int i,j,temp;
    for( i=1;i<n;i++)  //从第二个数到最后一个数 
    {
        if(a[i]<a[i-1])   //如果这个数比前一个数小 
        {
            temp=a[i];    //先把这个数保存下来 
            for( j=i;a[j-1]>temp;j--)   //拿着保存好的这个数temp从他开始往前去比较 ,如果前面的数还是比他temp小 
            {
                a[j]=a[j-1];   //和temp比过的数先后移动一位 
            }
            a[j]=temp;
        }
    }
} 

4.桶排序

#include <stdio.h>
int main()
{
    int a[100]={0}; //初始化为0,数字范围在0-100  
	int i,j,t;
     
    for(i=1;i<=5;i++)  //循环读入5个数,对5个数进行排序 
    {
        scanf("%d",&t);  //把每一个数读到变量t中
        a[t]++;  //进行计数(核心行)
    }

    for(i=0;i<=100;i++)  //依次判断a[0]~a[100]
        for(j=1;j<=a[i];j++)  //出现了几次就打印几次
            printf("%d ",i);


    return 0;
}

5.其他排序算法及稳定性、时间复杂度、空间复杂度:快快快点我

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);
}

上题解!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* convertAndMerge (char a[][40])
{
	int i,j;
	char *q=(char*)malloc(sizeof(char)*40);
	char *p;
	q=strcat(a[0],a[1]);
	for(p=q;*p!='\0';p++){
		if(*p>='a'&&*p<='z') *p-=32;
		else if(*p>='A'&&*p<='Z') *p+=32;
	}
	
	return q;
}

int main (void)
{
	char words[2][40]={"Welcome to Xiyou"," Linux Group 2022"};
	char *str=convertAndMerge(words);
	printf("%s",str);
	 
	free(str);
}
  • 这里有一个注意点,就是由于是要将两个字符串连接,此时的字符串长度已经超过了原定的20,所以要将字符数组的长度设为40,以保证不会越界

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("%4d", arr[i][j]);
    }
    printf("\n");
  }
}
  • main函数里的int argc, char **argv下文解释
  • 第二个for循环中出现的 arr[5] 其实是不存在的,但在这里可以理解为 temp<=arr[4][4]
  • 当i=0时,指针temp指向arr数组的首地址(即arr[0][0]),然后进入第二个for循环,将a=0赋值给arr[0][0],接着a++;temp++,以此类推,一直到temp=arr[5]时结束循环。此时数组arr按顺序被赋值为0~24(即arr[0][0]=0,arr[0][1]=1,…,arr[4][3]=23,a[4][4]=24)
  • 当i=1时,指针temp指向arr数组的第二行首地址(即arr[1][0]),然后进入第二个for循环,将a=25赋值给arr[1][0],接着a++;temp++,以此类推,一直到temp=arr[5]时结束循环。此时数组arr按顺序被新赋值为25~44(即arr[1][0]=25,arr[1][1]=26,…,arr[4][3]=43,a[4][4]=44)
  • 当i=2、3、4时同理可得
  • 最后输出程序,结果如下
    在这里插入图片描述

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;
        }
    }
}
  • argc:外部输入的参数个数
  • argv:参数的字符串数组,用来存放指向字符串参数的指针数组
  • (argc=1时,表示只有一个程序名称,存储在argv[0]中;argv[0]指向程序运行的全部路径名;argv[1]指向程序在DOS命令中执行程序名的第一个字符串,后面以此类推)
  • 此程序将会出现死循环:程序进入while循环后,每轮argc++,当argc加到231-1后,由于argc是int类型,有正负之分,且此时已达到int的最大值,所以当argc再+1后,argc=-231;然后接着在循环中进行argc++,直到argc加到-1时,此时再+1,argc=0,从此无限循环下去
  • 示意图见下

在这里插入图片描述

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);
}
  • 这里涉及到一个知识点:大端法,小端法
  • 在了解这个之前,先了解一个专业词:字节序。字节序,顾名思义就是字节的排列顺序,而计算机中既可以从高位到低位进行排列,也能从低位到高位进行排列
  • 现在再来看大端法、小端法:假设有数据0x12345678,左边为高字节,右边为低字节。将高字节的数据放在低地址,便是大端法;反之,为小端法

  • 下图,上面一排为字节的存放地址,从左到右,地址由低到高;下面一排为字节,分别存放在不同的地址中
  • 大端法
    在这里插入图片描述
  • 小端法
    在这里插入图片描述

  • 一般家庭计算机所使用的为小端法,因此根据以上知识并查阅阿斯克码表可得,该程序的输出为Welcome to Xiyou Linux Group 2022

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);
}
  • 理解:# 为预处理命令,正式编译前由系统自动完成。如果不用#define,要修改一个量,则代码中出现几次就修改几次,反之,只要修改一次即可。
  • 问:使用#define还有其他的好处吗?
  • 答:与在main函数中定义相应的变量相比,#define的执行时间会更短、反应更快,且宏只是源代码的替换,不会占用内存
  • 第一个printf的输出为2 1 1
    —简单交换不解释
  • 第二个printf的输出为1 2 2
    —进入if后,执行了SWAP中的第一个赋值后离开if语句,接着执行SWAP的其余两个赋值,完成了交换
  • 第三、四个printf的输出为2 2 5 5 2
    —由于SQUARE(a) a*a中,后面两个相乘的a自身没有加上(),导致a*a = 1 + 2 + z++ + ++w * 1 + 2 + z++ + ++w,而不是(1 + 2 + z++ + ++w) * (1 + 2 + z++ + ++w)。因此计算结果不等于100,无法进入SWAPWHEN的if语句中,从而无法执行SWAP的第一个赋值,只能跳过if语句,来执行SWAP的其余两个赋值

该如何修改才能使if语句控制整个SWAP,来达到交换或不交换的目的?
  • 解决方法:将swap修改成SWAP(a,b,t)( t = a; a = b; b = t )即可
  • 更多#define的易错易混点:狂戳此处!!!

14. GNU/Linux命令 (选做)

你知道以下命令的含义和用法吗:

注:

嘿!你或许对Linux命令不是很熟悉,甚至你没听说过Linux。
但别担心,这是选做题,不会对你的面试产生很大的影响!
了解Linux是加分项,但不了解也不扣分哦!

  • ls
  • rm
  • whoami

请问你还了解哪些GNU/Linux的命令呢。

  • ls: (list) 查看当前目录下的所有目录和文件

  • rm: (remove) 删除一个目录中的一个或多个文件或目录

  • whoami: (who am I) 显示自身的用户名称,相当于执行"id -un"指令

  • 更多Linux常用命令 :你懂得,点TA!


全题终。。。。。。

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值