2022~2021西邮LInux兴趣小组面试题 题解

2022

0. 我的计算器坏了?!

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

对应于十进制的k位等价于除以10k次小于1,中的最小值

两遍取对数进行运算


约等于0.30102999566398119521373
n=10000,代入得k为3010+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("")));
}

printf()返回打印字符的个数
输出: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));
}

p0[]分配内存储存字符串,*p1指针指向字符串,p2[11]给的空间不够(字符串末尾有\0)
p0和p1是指针,比较地址,明显不一样。p0==p1返回0
p2储存的字符串没有结尾,所以strcmp(p0,p1)无法返回0
sizeof测字符串大小时读取首地址。而*p2只是字符串的第一个元素,大小为1
strlen()函数返回字符串的长度(不包括\0)

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

作用域描述程序中可访问标识符的区域,生命周期常常用存储期描述。
写在所有函数的外面具有文件作用域,具有文件作用域的变量也被称为全局变量。文件作用域变量具有静态存储期。从程序被载入到程序结束期间都存在。
定义在块中的变量具有块作用域,这些变量往往被称为局部变量,具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存;当退出这个块时,释放刚才为变量释放的内存。

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

union各成员共享一块内存空间
struct各成员各有内存空间。但可能会内存对不齐,浪费内存。
结构体的内存对齐 - (jianshu.com)

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

位运算:先把10进制转化为2进制,然后再算(负数表示为补码,等于原码的取反加一)

| 位或 有一个1就是1
&位与 必须2个都是1才是1
^异或 相同为0,不同为1
~按位取反 0变1,1变0
<<左移 右移>>

注意char之类的本身就有范围,可能会有截断。

参考输出
a:56,b:0,c:254,d:48
e:0x4

6. 英译汉

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

  1. char *const p
  2. char const *p
  3. const char *p

1 > const在 * 右边修饰指针。
指向char类型的指针,不能更改指向的值
2、3 > const在 * 左边修饰char。
指向char类型的指针,不能修改指向的地方
const可以防止数据被意外篡改

7. 汉译英

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

  1. 含有10个指向int的指针的数组。
  2. 指向含有10个int数组的指针。
  3. 含有3个「指向函数的指针」的数组,被指向的函数有1个int参数并返回int

1 > int a[10]
2 > int (
p)[10]
3 > int *a[3] (int)

8. 混乱中建立秩序

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

提示:动动你的小手敲出来更好哦~

冒泡排序
像冒泡一样,元素一个个上来(被排序)。
稳定性:稳定
时间:O(n2) 空间:O(1)只用了一个临时变量
选择排序
每次选个最值
稳定性:稳定(正序遍历)/不稳定(倒序遍历)
时间:O(n2) 空间O(1)同样也只用了一个临时变量
归并排序
先分解再综合
稳定性:稳定
时间:O() 每一层n,总共层
空间:O(n)等于原来要排序的大小

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  
#include "stdlib.h"
#include 
#include 

char* ConvertAndMerge(char str[][20]);

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);
    return 0;
}
char* ConvertAndMerge(char str[][20])
{
    char *start,*s;
    start=s=(char*)malloc(strlen(str[0])+strlen(str[1])+1);
    char *p1=str[0];
    char *p2=str[1];
    while (*p1 != '\0')
    {
        if (isupper(*p1))
            *s++=tolower(*p1++);
        else
            *s++=toupper(*p1++);
    }
    while (*p2 != '\0')
    {
        if (isupper(*p2))
            *s++=tolower(*p2++);
        else
            *s++=toupper(*p2++);
    }
    return start;
}

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\t", arr[i][j]);
    }
  }
}

arr=&arr[0],*(arr+i)的意思是移动 i 行。
C保证中超出数组一个元素的指针指向的位置依然有效,arr[5]是第5行第1个元素的位置(从第0行开始数)。
第一次—— temp指向第0行,因为二维数组是以一维数组的方式排列的,temp从[0][0]到[5][0]总共重复了25次。所以输出后的二维数组被改成了这样:
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24 25

第二次—— temp指向第1行,a在第一次循环的历练之中已经变成了26,,现在再次循环,从[1][0]到[5][0]总共20次。第二次会对第一次的数据进行覆盖。所以结果如下:
0 1 2 3 4
26 27 28 29 30
31 32 33 34 35
36 37 38 39 40
41 42 43 44 45

在这之后的几次改动效果同上

改动之后二维数组的元素被依次打印了出来。
最后输出→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

11. 奇怪的参数

你了解argc和argv吗?
直接运行程序argc的值为什么是1?
程序会出现死循环吗?

#include 
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。
程序不会出现死循环,因为int类型的值有范围,超出范围就会发生溢出。溢出的结果是未定义的,但是很有可能是一个“最负的值”,于是满足(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);
}

本题涉及到大端模式和小端模式
小端模式低位字节排放在内存的低地址端,而大端模式则刚好相反。计算机内部大部分采用小端模式。
strcpy把a复制到buf,strcat再将b与buf拼接。
char类型一次读取一个字节,二个16进制位=8个2进制位,即1个字节。
0x636c6557 左端是高位字节,右端是低位字节(你可以拿10进制类比),
内存的读写永远从低地址开始读/写,所以它被2个2个地从后向前读。

最后输出→Welcome to Xiyou Linux Group 2022

13. 小试宏刀

  • 请谈谈你对#define的理解。
  • 请尝试着解释程序的输出。
#include 
#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);
}

把宏替换成对应的字符串
一板一眼地照着一点一点抽丝剥茧,很容易发现SWAPWHEN破绽百出
if(1 + 2 + z++ + ++w*1 + 2 + z++ + w++ == 100)
t=a;a=b;b=t;
if条件显然不对,t=a无法执行。但是后面两条没有被if给控制,可以正常执行。

14. GNU/Linux命令 (选做)

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

  • ls
  • rm
  • whoami

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

ls:查看当前目录下文件内容
rm:用于删除一个文件或目录(若删除目录需要配合 -r )
whoami:查看当前目录名
其他
timedatectl:查看当前时区设置
mkdir:新建一个文件夹
pwd:打印当前目录

2021(1)

1. 大小和长度竟然不是一个意思

sizeof()strlen()有什么异同之处?

他们对于不同参数的结果有什么不同?请试举例子说明。

int main(void) {
  char s[] = "I love Linux\0\0\0";
  int a = sizeof(s);
  int b = strlen(s);
  printf("%d %d\n", a, b);
}

sizeof()返回占空间的大小(包括\0),strlen()返回字符串长度(不包括\0)

2. 箱子的大小和装入物品的顺序有关

test1test2都含有:1个short、1个int、1个double,那么sizeof(t1)sizeof(t2)是否相等呢?这是为什么呢?

struct test1 {
  int a;
  short b;
  double c;
};
struct test2 {
  short b;
  int a;
  double c;
};
int main(void) {
  struct test1 t1;
  struct test2 t2;
  printf("sizeof (t1) : %d\n", sizeof(t1));
  printf("sizeof(t2): %d\n", sizeof(t2));
}

变量在内存中存放变量位置有限制,是大小的整数倍;
结构体的对齐值使其成员的最大对齐值。

输出→16 16

详情见上 [2022/4内存对不齐]

3. 哦,又是函数

想必在高数老师的教导下大家十分熟悉函数这个概念。那么你了解计算机程序设计中的函数吗?请编写一个func函数,用来输出二维数组arr中每个元素的值。

/*在这里补全func函数的定义*/
int main(void) {
  int arr[10][13];
  for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 13; j++) {
      arr[i][j] = rand();
    }
  }
  func(arr);
}

参考代码:

void func(int arr[][13],int n,int m)
{
    int i,j;
    for(i=0;i

4.就不能换个变量名吗?

  • 请结合下面的程序,简要谈谈传值传址的区别。
  • 简要谈谈你对C语言中变量的生命周期的认识。
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;
    func1(ver);
    int ver = 7;
    printf("ver = %d\n", ver);
    func2(&ver);
  }
  printf("a = %d\tver = %d\n", a, ver);
}

传值:把实参的值copy给形参,相当于生成一个影分身,“分身受伤对本体没有影响”。但是要用一块新的空间,比较浪费。
传址:把地址传给别人,会被顺着地址找上门来,所以实参可能会被改变。

生命周期:[2022/3. 换个变量名不行吗?]

运行结果→
a = 5
ver = 1026
ver = 7
*pr = 1234
ver = 123
a = 0 ver = 1025

5. 套娃真好玩!

请说明下面的程序是如何完成求和的?

unsigned sum(unsigned n) { 
    return n ? sum(n - 1) + n : 0;}
int main(void) { 
    printf("%u\n", sum(100));
}

这是一个递归
直到n==0为止,调用sum(n-1)
n
n n-1
n n-1 n-2
n n-1 n-2 n-3
n n-1 …………0
n n-1 n-2 n-3
n n-1 n-2
n n-1
n
这里输入的参数为100,结果为0+1+2+……100=5050

6. 算不对的算术

void func(void) {
  short a = -2;
  unsigned int b = 1;
  b += a;
  int c = -1;
  unsigned short d = c * 256;
  c <<= 4;
  int e = 2;
  e = ~e | 6;
  d = (d & 0xff) + 0x2022;
  printf("a=0x%hx\tb=0x%x\td=0x%hx\te=0x%x\n", a, b, d, e);
  printf("c=0x%hhx\t\n", (signed char)c);
}

运算规则见 [2022/5 Bitwise]
简单的运算,没什么好说的。
%h short ; %hh char

7. 指针和数组的恩怨情仇

int main(void) {
  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));
}

b指向&a[0]
++b指向&a[1]
**(a+1)第1行第0个元素
*(ptr-1)第2行第2个元素(ptr一次移动一个表)
结果→ 10 4 9

8. 移形换位之术

下面有abc三个变量和4个相似的函数。

  • 你能说出使用这三个变量的值或地址作为参数分别调用这5个函数,在语法上是否正确吗?
  • 请找出下面的代码中的错误。
  • const intint const是否有区别?如果有区别,请谈谈他们的区别。
  • const int *int const *是否有区别?如果有区别,请谈谈他们的区别。
int a = 1;
int const b = 2;
const int c = 3;
void funco(int n) {
  n += 1;
  n = a;
}
void func1(int *n) {
  *n += 1;
  n = &a;
}
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;
}

答案都是没区别 见[2022/6 英译汉]
func2×
const说明指针指向的值不能改,它却依然改
func3×
const说明指针指的位置不能变,却依然变
其他都对√

9. 听说翻转字母大小写不影响英文的阅读?

请编写convert函数用来将作为参数的字符串中的大写字母转换为小写字母,将小写字母转换为大写字母。返回转换完成得到的新字符串。

char *convert(const char *s);
int main(void) {
  char *str = "XiyouLinux Group 2022";
  char *temp = convert(str);
  puts(temp);
}

见[2022/9. 手脑并用]

10. 交换礼物的方式

  • 请判断下面的三种Swap的正误,分别分析他们的优缺点。
  • 你知道这里的do {...} while(0)的作用吗?
  • 你还有其他的方式实现Swap功能吗?
#define Swap1(a, b, t) 
  do {                 
    t = a;             
    a = b;             
    b = t;             
  } while (0)
#define Swap2(a, b) 
  do {              
    int t = a;      
    a = b;          
    b = t;          
  } while (0)
void Swap3(int a, int b) {
  int t = a;
  a = b;
  b = t;
}

3不行,1和2可以
3复制了分身,对本体没有产生任何影响。
swap1 多传一个参,但不创建新变量
swap2 方便
do{}while(0)保证只交换一次
还可以借助位运算进行交换:

a^=b;b^=a;a^=b;

解释
一个数异或它自己等于0;
0异或任何数等于那个数本身;
a=a ^ b
b=b ^ a ^ b=b ^ b ^ a=0^a=a
a=a ^ a ^ b=0 ^ b=a

11. 据说有个东西叫参数

你知道argcargv的含义吗?请解释下面的程序。你能在不使用argc的前提下,完成对argv的遍历吗?

含义见[2022/11. 奇怪的参数]
参考代码:

int main(int argc, char *argv[]) {
  printf("argc = %d\n", argc);
  for (int i = 0; i < argc; i++)
    printf("%s\n", argv[i]);
}

12. 人去楼空

这段代码有是否存在错误?谈一谈静态变量与其他变量的异同。

int *func1(void) {
  static int n = 0;
  n = 1;
  return &n;
}
int *func2(void) {
  int *p = (int *)malloc(sizeof(int));
  *p = 3;
  return p;
}
int *func3(void) {
  int n = 4;
  return &n;
}
int main(void) {
  *func1() = 4;
  *func2() = 5;
  *func3() = 6;
}

func1 func2正确 ;
func3错误,在块中分配给n的内存函数执行完后就被还回去了。

静态变量,静态的意思不是说它的值不变,而是说它在内存中原地不动。这些变量和自动变量一样都具有块作用域,即只能在块中通过标识符访问。但是如本题中func1中的static声明的变量一样,它所占的空间不会因为程序离开了它所在的函数就被自动返还。
其他相关知识 见[2022/3. 换个变量名不行吗?]

13. 奇怪的输出

int main(void) {
  int data[] = {0x636c6557, 0x20656d6f, 0x78206f74,
                0x756f7969, 0x6e694c20, 0x67207875,
                0x70756f72, 0x32303220, 0x00000a31};
  puts((const char*)data);
}

见[2022/12. 奇怪的字符]

14. 请谈谈对从「C语言文件到可执行文件」的过程的理解

C通过编译和链接两个步骤来完成这一过程
编译器把源代码转成中间代码(汇编),链接器把中间代码和其他代码合并,生成可执行文件。
{这里的其他文件包括:
预编译的库代码
系统的标准启动代码(启动代码充当着程序和操作系统之间的接口)
}

这种分而治之的方法有助于对程序进行模块化,让你可以独立编写一个新模块,然后再用编译器将其与已编译的模块链接起来。在有些系统中,必须分别运行编译程序和链接程序,而在另一些系统中,编译器会自动启动链接器,用户只需给出编译命令即可。

15. (选做) 堆和栈

你了解程序中的栈和堆吗?它们在使用上有什么区别呢?请简要说明。

内存分类:
栈区(stack) 由编译器自动分配释放
堆区(heap) 由程序员自己指定大小,自己释放
全局区(静态区)(static) 程序结束后由系统释放
文字常量区: 程序结束后由系统释放

申请内存后,
当栈的剩余空间>申请空间时,系统就会为程序提供内存,否则将提示栈溢出。
堆会遍历记录空间内存地址的链表,搜索一个空间比所申请空间大的堆节点,将该节点的空间分配给程序。

16. (选做) 多文件

一个程序在不使用任何头文件的情况下,如何使用另一个文件中的函数。

被引用的函数的返回值类型前面添加extern关键字,表明该函数可以被其他的源文件调用
然后在去引用那个函数程序中添加要用到的函数原型

17. (选做) GNU/Linux与文件

  • 你知道如何在 GNU/Linux下如何使用命令行创建文件与文
    件夹吗?
  • 你知道GNU/Linux下的命令ls 的每一列的含义吗?
  • 你知道GNU/Linux下文件的访问时间、修改时间、创建时间如何查看吗?并简单说说他们的区别。

创建文件夹:mkdir 目录名,一次只能创建一层,如果使用-p(递归)选项可以一次创建多层
创建文件:touch

ls -I
1.文件属性
2.文件个数
3.该文件或目录的拥有者
4.所属的组
5.文件大小
6.创建日期
7.文件名

时间相关:https://blog.csdn.net/zyz511919766/article/details/14452027

2021(2)

1. 请试着解释其输出。

int main(int argc , char *argv[]) {
  unsigned char a = 255;
  char ch = 128;
  a -= ch;
  printf("a = %d ch = %d\n", a, ch);
}

char的范围是 -128~127;ch越界,真正的值为 -128。
为什么-128的补码是1000 0000? - (jianshu.com)
a-ch=255-128=127

2. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  char *str = "Xi You Linux Group 20";
  printf("%d\n", printf(str));
  return 0;
}

见[2022/1. printf还能这么玩?]
输出→Xi You Linux Group 2021

3. 这段代码的输出结果是什么?为什么会出现这样的结果?

int i = 2;
void func() {
  if(i != 0) {
  static int m = 0;
  int n = 0;
  n++;
  m++;
  printf("m = %d, n = %d\n", m, n);
  i--;
  func();
  } else {
  return;
  }
}
int main(int argc, char *argv[]) {
  func();
  return 0;
}

输出→
m = 1, n = 1
m = 2, n = 1
变量的生命周期
见[2022/3. 换个变量名不行吗?] [2021/12. 人去楼空]

4. 下面程序会出现什么结果?为什么会出现这样的结果?

int main(int argc, char * argv[]) {
  char ch = 'A';
  int i = 65;
  unsigned int f = 33554433;
  *(int *)&f >>= 24;
  *(int *)&f = *(int *)&f + '?';
  printf("ch = %c i = %c f = %c\n", ch, i, *(int *)&f);
  return 0;
}

输出→ch = A i = A f = A
*(int *)&f意为采用读int的方法读f,然后再给出用这种方式读出来的f(相当于强制转换为int类型)
?的ASCII码等于63
其他就是简单的位运算。

5. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  int a[2][2];
  printf("&a = %p\t&a[0] = %p\t&a[0][0] = %p\n", &a, &a[0], &a[0][0]);
  printf("&a+1 = %p\t&a[0]+1 = %p\t&a[0][0]+1= %p\n", &a+1, &a[0]+1, &a[0][0]+1);
  return 0;
}

数组名等于它的地址,数组的地址是它第一个元素的地址,二维数组的元素可以看做一维数组。
输出→
&a = 000000000061FE10 &a[0] = 000000000061FE10 &a[0][0] = 000000000061FE10
&a+1 = 000000000061FE20 &a[0]+1 = 000000000061FE18 &a[0][0]+1= 000000000061FE14

6. 下列程序的功能是什么?有什么问题,你能找出问题并解决它吗?

int* get_array() {
  int array[1121]; 
  for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
    array[i] = i;
  }
  return array;
}
int main(int argc, char *argv[]) { 
  int *p = get_array();
}

array的内存被还回去了,指针指了个寂寞,所以这个程序没有用。
可以使用malloc手动分配空间,参考代码:

int* get_array() 
{
    int*array=NULL,*save=NULL;
    save=array=(int*)malloc(sizeof(int)*1121);
    for (int i = 0; i < 1121; i++,array++) 
        *array = i;
    return save;
}

7. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  char str[] = "XiyouLinuxGroup"; 
  char *p = str; 
  char x[] = "XiyouLinuxGroup\t\106F\bamily";
  printf("%zu %zu %zu %zu\n", sizeof(str), sizeof(p), sizeof(x), strlen(x));
  return 0;
}

\t(tab) \106(F) \b(退格)分别占一个字节,相当于char类型
其他见 [2022/2. 你好你好你好呀!]
输出:16 8 25 24

8. 如下程序,根据打印结果,你有什么思考?

int add(int *x, int y) {
  return *x = (*x^y) + ((*x&y)<<1);
}
int a;
int main(int argc, char *argv[]) {
  int b = 2020;
  if(add(&b, 1) || add(&a, 1)) {
  printf("XiyouLinuxGroup%d\n", b);
  printf("Waiting for y%du!\n", a);
  }
  if(add(&b, 1) && a++) {
  printf("XiyouLinuxGroup%d\n", b);
  printf("Waiting for y%du!\n", a);
}
  return 0;
} 

没什么思考,只是感觉花里胡哨的
位运算(&、|、^、~、>>、 | 菜鸟教程 (runoob.com)

9. 在下段程序中,我们可以通过第一步打印出a的地址,假如在你的机器上面打印结果是0x7ffd737c6db4;我们在第二步用scanf函数将这个地址值输入变量c中;第三步,随机输入一个数字,请问最终输出了什么结果,你知道其中的原理吗?

void func() { 
  int a = 2020;
  unsigned long c;
  printf("%p\n", &a);
  printf("我们想要修改的地址:");
  scanf("%lx", &c);
  printf("请随便输入一个数字:");
  scanf("%d", (int *)c);
  printf("a = %d\n", a);
}

scanf("%lx",&c)把a的地址给c
(int *)c使c强制转化为一个指向整型的指针
于是a被这个指针赋值为那个输入的值

10. 请问一个C语言程序从源代码到可执行文件中间会进行哪些过程,你能简单描述一下每个环节都做了什么事情吗?

同【2021/14. 请谈谈对从「C语言文件到可执行文件」的过程的理解】

11. 请解释一下这行代码做了什么?

puts((char*)(int const[]){
0X6F796958,0X6E694C75,0X72477875,
0X3270756F,0X313230,0X00000A
});

打印 XiyouLinuxGroup2021
见[2022/12. 奇怪的字符]

12. 请随机输入一串字符串,你能解释一下输出结果吗?

int main(int argc, char *argv[]) {
  char str[1121];
  int key;
  char t;
  fgets(str, 1121, stdin);
  for(int i = 0; i < strlen(str) - 1; i++) {
    key = i;
    for(int j = i + 1; j < strlen(str); j++) {
      if(str[key] > str[j]) {
        key = j;
      }
    } 
    t = str[key];
    str[key] = str[i];
    str[i] = t;
  } 
  puts(str);
  return 0;
}

对输入的字符进行一个排序
利用的是选择排序,在(str-i-1)个数中,遍历找到最小值后跟第i个字符个字符进行交换。
char *fgets(char *str, int n, FILE *stream):从指定的流里读取一行字符串,然后把它存储在str指向的字符串之中。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

13. 用循环和递归求Fibonacci数列,你觉得这两种方式那种更好?说说你的看法。如果让你求Fibonacci数列的第100项,你觉得还可以用常规的方法求解吗?请试着求出前100项的值(tip大数运算)。

递归虽然简洁,但斐波那契数列需要用到双递归,用到的空间会飞速增长。
循环代码比较臃肿,看着丑。
总体来说,我认为循环比较好。

大数运算其实就是用代码模拟竖式计算
大斐波那契数_陶鸿杰的博客-CSDN博客

14. Linux 实操题

请通过命令创建一个目录,在该目录中创建几个后缀为.Linux的文件,然后通过命令查询这几个文件的基本属性信息(如文件大小,文件创建时间等),之后使用命令查看该目录下文件名含有“.Linux”的文件的数量(不包括子目录下的文件),把得到的数字写入到一个文件中,最后删除此目录。

mkdir fold
cd fold
touch a.Linux
touch b.Linux
ls -I
echo "2" > a.Linux
cd ..
rm -r fold

你可能感兴趣的:(2022~2021西邮LInux兴趣小组面试题 题解)