目录
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
}
题解:
结构体的对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
联合大小的计算:
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
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
的作用。
- char *const p。
- char const *p。
- const char *p。
题解:
①const的含义:
*
在const
前,p不能指向其他对象,但此对象的值可以被改变
2. *
在const
后,p指向的对象的值不能被改变,但可以重新指向其他的对象
②const的作用:
防止原本不想被改变的变量被改变
7. 汉译英
请用变量
p
给出下面的定义:
1.含有10个指向
int
的指针的数组。
2.指向含有10个int
数组的指针。
3.含有3个「指向函数的指针」的数组,被指向的函数有1个int
参数并返回int
。
题解:
int*p[10]
p先和[10]结合,表明p是一个数组,因此是一个int*型的指针数组int(*p)[10]
p和*结合,表明p是一个指针,因此p是一个数组指针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
考点:
- 大小端字节序
- (char*)型强制类型转换的应用
- 对照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);
}
题解:
- 预处理命令,正式编译前由系统自动完成。
- 第三行:翻译过来是: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"指令