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

0. 我的计算器坏了?!

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

题解:

考点: 2^10000 大于long long类型,无法存储,只能用字符串计算

解法:
直接数学计算:第n位的位数=n*log2+1
ps: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("")));
}

题解:

//考点:printf()的返回值
int main(void)
{
	if ((3 + 2 < 2) > (3 + 2 > 2))    //if(0>1) --> 为假,进入else
		printf("Welcome to Xiyou Linux Group\n");
	else
		printf("%d\n", printf("Xiyou Linux Group - 2%d", printf(""))); //嵌套进去的两个printf打出的字符个数依次0和22,
		                                                               //因此输出内容为:Xiyou Linux Group - 2022
	return 0;
}

//输出内容:Xiyou Linux Group - 2022

//1.printf()的返回值是输出的字符数量,包括数字,字母,标点符号,空格等。('\n'也是哦)

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

题解:

int main(void)
{
	char p0[] = "Hello,Linux";    //栈区
	char* p1 = "Hello,linux";     //常量区
	char* p3 = "Hello,linux";
	char p2[11] = "Hello,Linux";
	printf("p0 == p1: %d,strcmp(p0,p2): %d\n", p0 == p1, strcmp(p0, p2));  //0 -1  //"字符串常量"存在常量区  //strcmp的返回值:
	printf("sizeof(p0):%zu,sizeof(p1):%zu,sizeof(*p2):%zu\n", sizeof(p0), sizeof(p1), sizeof(*p2));   //12 4 1
	printf("strlen(p0):%zu,strlen(p1):%zu\n", strlen(p0), strlen(p1));  //11 11


    //字符串常量存在常量区,p1指向这个字符串常量,常量区的内容不能被更改
	//第一句相当于定义了一个字符数组,但是这个p0数组是存放在栈区的,然后再把字符串常量"Hello,Linux"拷贝到栈区的p0数组内,那么此时的p0是可以修改的。
}

//说明:%zu转换为size_t类型

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

题解:

// 考点:全局变量和局部变量的生命周期 
int a = 3;
void test()
{
	int a = 1;
	a += 1;
	{
		int a = a + 1;          //使用了未初始化的局部变量
		printf("a = %d\n", a); 
	}
	printf("a = %d\n", a); //2
}
int main(void)
{
	test();
	printf("a = %d\n", a);  //3
}

//注意:
//1.变量在作用时先找离他最近的作用域的并且可以使用的变量
//2.当局部变量和全局变量重名时,优先使用局部变量

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));    //20
    printf("sizeof (STRUCT) = %zu \n", sizeof(STRUCT));  //32
}

题解:

结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

联合大小的计算:

  1. 联合的大小至少是最大成员的大小。
  2. 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

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

题解:

//考点:运算符的使用,数据的存储
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;  //这个地方对(a^c)进行了计算,要进行整形提升
	             //d:0000 0110 0011 0000
	signed char e = -63;
	e <<= 2;

	printf("a:%d,b:%d,c:%d,d:%d\n", a, b, c, (char)d);   //56 0 254 48
	                                     //强转后d:0011 0000  -> 48
	printf("e:%#x\n", e);   //0x4   //记得打印要把补码转换为原码,打印记得要整形提升,截断后
}

//%#x的意思是,以16进制打印,前面带0x

6. 英译汉

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

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

题解:

①const的含义:

  1. *const前,p不能指向其他对象,但此对象的值可以被改变
    2. *const后,p指向的对象的值不能被改变,但可以重新指向其他的对象
    ②const的作用:
    防止原本不想被改变的变量被改变

7. 汉译英

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

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

题解:

  1. int*p[10] p先和[10]结合,表明p是一个数组,因此是一个int*型的指针数组
  2. int(*p)[10] p和*结合,表明p是一个指针,因此p是一个数组指针
  3. int(*p[3])(int) p首先和[3]结合表明p是一个数组,在于*结合表明p是一个指针数组,(int)表示函数的参数,表明被指向的函数有一个int的参数,最前面的int表明返回类型是int,即p是一个函数指针数组

注意:符号的优先级为:()> [ ] > *

8. 混乱中建立秩序

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

题解:

1.冒泡排序

void bubble_sort(int arr[], int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		int flag = 0;
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 1;
			}
		}
		if (flag == 0)
		{
			break;
		}
	}
}

2.选择排序


void select_sort(int *p,int n)
{
    int i,j;
    int min = 0;
    for(i = 0;i < n - 1;i++)//排序次数
    {
        min = i;
        for(j = i + 1;j < n;j++)
        {
            if(p[j] < p[min])
            {
                min = j;//记录交换的元素下标值
            }
        }
        if(i != min)
        {
            int temp = p[i];
            p[i] = p[min];
            p[min] = temp;
        }  
    }
}

3.插入排序

void insert_sort(int arr[], int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		int end = i;       //记录已排序好的最后一个元素的下标
		int tmp = arr[end + 1];   //记录下一个待排序数字
		while (end >= 0)   //其中一种退出循环的方式,这个待排数比前面所有有序数字都小
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}

		arr[end + 1] = tmp;

	}
}

4.快速排序

void quick_sort(int arr[], int begin, int end)   //begin和end随着递归,在不断的变化
{
	if (begin >= end)   //递归的终止条件,排序到只剩下一个元素
	{
		return;
	}

	int left = begin;
	int right = end;

	int key = begin;

	while (begin < end)   //大循环,循环结束把选定的key值放入下标为end(right)的位置
	{
		//右边找
		while (arr[end] >= arr[key] && begin < end)   //右边选小,等号防止和key值相等,防止顺序begin和end越界
		{
			end--;
		}
		//左边找
		while (arr[begin] <= arr[key] && begin < end) //左边选大,等号防止和key值相等,防止顺序begin和end越界
		{
			begin++;
		}
		//交换左右找到的数,如果right和end相等就相当于自己和自己交换
		int tmp = arr[begin];
		arr[begin] = arr[end];
		arr[end] = tmp;

	}

	//把key值交换到在它该在的位置(end和right交界的地方)
	int tmp = arr[key];
	arr[key] = arr[end];
	arr[end] = tmp;

	key = end;

	quick_sort(arr, left, key - 1);
	quick_sort(arr, key + 1, right);
}

最后附上一个测试用的代码:

int main()
{
	int arr[50] = { 0 };
	int n = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &arr[i]);
	}
	
	//bubble_sort(arr, n);
	//时间复杂度:最坏情况:O(N^2)
	//最好情况:O(N)
	//空间复杂度:O(1)
	// 
	//select_sort(arr, n);
	//时间复杂度:最坏情况:O(N^2)
	//最好情况:O(N^2)
	//空间复杂度:O(1)
	//
	//insert_sort(arr, n);
	//时间复杂度:最坏情况下为O(N * N),此时待排序列为逆序,或者说接近逆序
	//最好情况下为O(N),此时待排序列为升序,或者说接近升序。
	//空间复杂度:O(1)
	//
	quick_sort(arr, 0, n - 1);
    //时间复杂度:n*log2 n;

	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

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

题解:

//学一下四个动态内存开辟的函数吧
//1.malloc 2.calloc 3.free 4.realloc
#include<stdio.h>
#include<string.h>
char* convertAndMerge(char word[2][20])
{
	//用malloc开辟10个整形大小的空间
	char* p = (char*)malloc(40);

	strcpy(p, word[0]);
	strcat(p, word[1]);
	char* tmp = p;
	while (*p)
	{
		if (*p >= 'a' && *p <= 'z')
		{
			*p -= 'a' - 'A';
			p++;
		}
		else if(*p >= 'A' && *p <= 'Z')
		{
			*p += 'a' - 'A';
			p++;
		}
		else
		{
			p++;
		}
	}
	return tmp;
}

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

题解:

程序输出结果:
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 <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 是指命令行输入参数的个数,argv存储了所有的命令行参数。
argc的全称是 :arguments count(参数计数)
argv的全称是 :arguments value / vector(参数值)
argv[0] 指向程序运行时的全路径名
argv[1] 指向程序在DOS命令中执行程序名后的第一个字符串
argv[2] 指向执行程序名后的第二个字符串
argv是指向指针的指针,main函数的第二个参数“char * argv[]“也可以替换为 “char * *argv“,两者是等价的。
在C++中:一般编译器默认使用argc和argv两个名称作为main函数的参数,但这两个参数如此命名并不是必须的,
你可以使用任何符合C++语言命名规范的变量名作为入参。
如果定义mian方法时没有定义形参也没有关系,因为在stdlib.h头文件中定义了_argc和_argv两个变量可以使用。

直接运行程序argc的值为什么是1?
答:因为肯定至少得输入要执行的exe文件名吧,所以参数至少会有一个

程序会出现死循环吗?
答:会

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

题解:

输出结果为:Welcome to Xiyou Linux Group 2022

考点:

  1. 大小端字节序
  2. (char*)型强制类型转换的应用
  3. 对照ascii码表得出答案

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

题解:

  1. 预处理命令,正式编译前由系统自动完成。
  2. 第三行:翻译过来是:a*a = 1 + 2 + z++ + ++w * 1 + 2 + z++ + ++w,而不是(1 + 2 + z++ + ++w) * (1 + 2 + z++ + ++w)

14. GNU/Linux命令 (选做)

你知道以下命令的含义和用法吗:
注:
嘿!你或许对Linux命令不是很熟悉,甚至你没听说过Linux。
但别担心,这是选做题,不会对你的面试产生很大的影响!
了解Linux是加分项,但不了解也不扣分哦!

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

题解:

ls: (list) 查看当前目录下的所有目录和文件
rm: (remove) 删除一个目录中的一个或多个文件或目录
whoami: (who am I) 显示自身的用户名称,相当于执行"id -un"指令

点击此处,了解更多linux常用指令

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值