0. 我的计算器坏了?!
2^10 = 1024
对应于十进制的4位,那么2^10000
对应于十进制的多少位呢?
对10进制的数字取以10为底的对数再加1,即可得到其对应的十进制位数
lg2^10000+1=10000*lg2+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("")));
}
输出结果:Xiyou Linux Group-2022
3+2<2为假返回0,3+2>2为真返回1,0>1为假,所以不执行if语句,执行else语句
内层printf输出空字符并返回0,外层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));
}
输出结果:0 0 12 8 1 11 11
- p0,p1代表的都是地址,二者地址不同,返回0
- p0,p2中的内容相同,用strcmp比较字符串时,相等返回0
- p0中字符串总长度(加上\0)为:12
- p1是指针,在64位编译器下占8个字节
- p2代表首元素地址,对其解引用找到首元素H,占一个字节
- strlen计算字符串长度(不算\0),p0,p1字符串长度都为11
- sizeof是一个计算变量所占空间大小的操作符,计算时要算\0,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);
}
全局变量
在函数外部定义的,其使用范围从定义位置开始,一直到整个程序结束局部变量
定义在函数或复合语句(用大括号括起来的语句)内部生命周期
局部变量的生命周期从进它的作用域时开始,到离开作用域时结束;全局变量的生命周期是整个程序的运行期间- 第一次打印a时a没有初始化,编译器会给一个默认值
- 第二次打印的是test()下面定义的a,a自增后为2
- 第三次打印的是全局变量a,a=3
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));
printf("sizeof (STRUCT) = %zu\n", sizeof(STRUCT));
}
输出结果:20 32
union
long l占4个字节,int i[5]占20个字节,char c占1个字节,联合体内部变量共用一块内存,起始地址都相同,20刚好是4的倍数,所以结果为20struct
int like先申请4个字节,UNION coin再申请20个字节,此时24刚好是8的倍数,double collect再申请8个字节,24+8=32,32刚好是8的倍数,所以结果为32
内存对齐:数据成员对齐:每个数据成员都会按照其自身类型的大小,从其自身大小的整数倍内存地址开始存储。例如,如果一个结构体中有char类型的成员,那么该成员将紧随在前面的成员之后存储,而且其存储地址是char类型大小的整数倍。
结构体成员对齐:如果结构体中包含了其他结构体类型的成员,那么这些结构体成员的存储位置将从其内部成员最大值大小的整数倍地址处开始存储。例如,如果一个结构体中有一个包含int类型成员的结构体成员,那么该结构体成员的存储位置将是int类型大小的整数倍。
结构体总大小对齐:结构体的总大小必须是其内部最大成员大小的整数倍。如果结构体的总大小不是其内部最大成员大小的整数倍,那么编译器会插入填充字节,使得结构体的总大小达到最大成员大小的整数倍。
5. Bitwise
- 请使用纸笔推导出程序的输出结果。
- 请谈谈你对位运算的理解。
int main(void) {
unsigned char a = 4 | 7;//a=00000010|00000111=00000111=7
a <<= 3;//00111000=56
unsigned char b = 5 & 7;//00000101&00000111=00000101=5
b >>= 3;//00000000=0
unsigned char c = 6 ^ 7;//00000110^00000111=00000001
c = ~c;//11111110
unsigned short d = (a ^ c) << 3;//(00111000^11111110)=11000110<<3=00000110 00110000=1584
signed char e = -63;//10111111--11000000--11000001
e <<= 2;//00000100=48
printf("a: %d, b: %d, c: %d, d: %d\n", a, b, c, (char)d);
printf("e: %#x\n", e);
}
输出结果:
56 0 254 48
0x4(具体解析看上边代码中的注释)
- ~:按位取反
- <<:左移
- >>:右移
- &:有0取0,没有0取1
- |:有1取1,没有1取0
- ^:相同为0,不同为1
6. 英译汉
请说说下面数据类型的含义,谈谈
const
的作用。
char *const p
。char const *p
。const char *p
。
(1)char *const p
是一个指向字符的指针,指向的内容是常量,不能通过这个指针修改它所指向的字符的值,但可以改变指针本身所指向的内容
(2)char const *p
与const char *p
等价,,都是一个指向字符的常量指针,指向的内容是常量,不能通过这个指针修改它所指向的字符的值,也不能改变指针本身所指向的内容
7. 汉译英
请用变量
p
给出下面的定义:
- 含有10个指向
int
的指针的数组。- 指向含有10个
int
数组的指针。- 含有3个「指向函数的指针」的数组,被指向的函数有1个
int
参数并返回int
。
(1)int *p[10],p得先跟[ ]结合,证明本质是数组
(2)int(*p)[10],p得先跟解引用操作符结合,证明本质是指针
(3)int(*p[3])(int),p首先和[ ]结合表明p是一个数组,再与*结合表明是指针数组,前面的int是返回类型,最后面的int是参数类型
8. 混乱中建立秩序
你对排序算法了解多少呢?
请谈谈你所了解的排序算法的思想、稳定性、时间复杂度、空间复杂度。
提示:动动你的小手敲出来更好哦~
冒泡排序:
#include <stdio.h>
void bubble_sort(int arr[],int sz)
{
for(int i=0;i<sz-1;i++)
{
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;
}
}
}
}
int main()
{
int arr[]={1,4,2,5};
int sz=sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr,sz);
for(int i=0;i<sz;i++)
{
printf("%d",arr[i]);
}
return 0;
}
思想:两个相邻的元素进行比较
时间复杂度:O(n^2)
空间复杂度:O(1)
比较稳定
选择排序:
void selectsort(int*arr,int len)
{
int min;
int tmp;
for(int i=0;i<len-1;i++)
{
min=i;
for(int j=i+1;j<len;j++)
{
if(arr[j]<arr[min])
{
min=j;
}
}
if(min!=i)
{
tmp=arr[i];
arr[i]=arr[min];
arr[min]=tmp;
}
}
}
思想:找最大(小)的元素放在序列的开头
时间复杂度:O(n^2)
空间复杂度:O(1)
不稳定
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 <string.h>
#include <stdlib.h>
char* convertAndMerge(char s[2][20])
{
char *result=(char*)malloc(sizeof(char)*40);//分配空间
strcpy(result,s[0]);
strcat(result,s[1]);//拼接字符串
int len=strlen(s[0])+strlen(s[1]);
for(int i=0;i<len;i++)
{
if(result[i]>='A'&&result[i]<='Z')
{
result[i]+=32;
}
else if(result[i]>='a'&&result[i]<='z')
{
result[i]-=32;
}
}
return result;
}
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);
}
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]);
}
}
}
输出结果:
0 1 2 3 4
26 26 27 28 29
45 46 47 48 49
60 61 62 63 64
70 71 72 73 74
第一次循环输出0~24(第一行到第五行)
第二次循环输出25~44(第二行到第五行)
第三次循环输出45~59(第三行到第五行)
第四次循环输出60~69(第四行和第五行)
第五次循环输出70~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
是指向字符串数组的指针,每个字符串都是一个命令行参数,argv[0]是程序名- argc中第一个参数是程序名,所以argc至少为1
- 程序不会死循环,数据过大溢出后会变为负数,然后又增加到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);
}
输出结果:
Welcome to Xiyou Linux Group 2022
程序会把data[1]和data[2]中的字符串拼接起来,采取小端存储(低字节放在低地址,高字节放在高地址)的方式,最后打印字符串
13. 小试宏刀
- 请谈谈你对
#define
的理解。- 请尝试着解释程序的输出。
#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\n", x, y, tmp);
printf("z = %d, w = %d, tmp = %d\n", z, w, tmp);
}
输出结果:
x=2 y=1 tmp=1
x=1 y=2 tmp=2
x=2 y=2
z=5 w=5 tmp=2
主要考点:宏定义只是实现简单的文本替换,不会为其增添括号
- 第一个swap:tmp=x;x=y;y=tmp交换之后x=2 y=1 tmp=1
- x=2 y=1 x>y所以if语句执行:tmp=x;x=y;y=tmp 交换之后x=1 y=2 tmp=2
- 因为1+2+z++ + ++w*1+2+z++ + ++w=1+2+3+4*1+2+3+4!=100,所以只执行swap的后两条语句:x=y y=tmp交换之后x=2 y=2
- 上面z和w都进行过两次自增,所以z=5 w=5 tmp=2
- #define只是进行简单的文本替换,不会为表达式补充括号
14. GNU/Linux命令 (选做)
你知道以下命令的含义和用法吗:
注:
嘿!你或许对Linux命令不是很熟悉,甚至你没听说过Linux。
但别担心,这是选做题,不会对你的面试产生很大的影响!
了解Linux是加分项,但不了解也不扣分哦!
ls
rm
whoami
请问你还了解哪些GNU/Linux的命令呢。
- ls:显示指定工作目录下的内容
- rm:删除一个文件或目录
- whoami:打印当前执行操作的目录名