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**,因此输出为12
;sizeof(p1)
计算的是p1即指针的大小,在32位上结果为4
,64位上结果为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. 内存对不齐
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(联合体)内部的成员共享一段内存,因此联合体的大小满足:
①大小足够容纳最宽的成员(一般取决于其内部最大成员类型的长度*)
②大小能被其包含的所有基本数据类型的大小所整除
*注:以下例子并不满足①中的一般条件,而是要用到第②点
结果为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
的作用。
char *const p
。char const *p
。const char *p
。
- 第一个:相当于char p[],p的地址不能变,但*p的值可以改变
- 第二、三个:它们等价,均声明了一个指向字符常量的指针(因此不能改变它所指的字符),*p的值不能变,p的地址可以改变
- const的作用:
1.给读代码的人这个参数的应用目的
2.使函数能处理const和非const的实参
3.能产生更加紧凑的代码
4.保护被修饰的东西
这,,,没看够? >> 还是戳这里啦
7. 汉译英
请用变量
p
给出下面的定义:
- 含有10个指向
int
的指针的数组。- 指向含有10个
int
数组的指针。- 含有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. 混乱中建立秩序
你对排序算法了解多少呢?
请谈谈你所了解的排序算法的思想、稳定性、时间复杂度、空间复杂度。
- 冒泡排序
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!
全题终。。。。。。