1.gcc的编译流程一般分为:
预处理、编译、汇编、链接。
hello.c 经过预处理可以得到 hello.i 使用 gcc -E hello.c –o hello.i 命令。
经过编译得到hello.s 使用 gcc -S hello.i 命令 得到汇编文件。
经过汇编得到hello.o 使用 gcc -c hello.s 命令 得到机器码文件。
经过链接得到hello(a.out)文件 使用gcc hello.o 命令 得到a.out,即可执行文件。
2.简述 阻塞 非阻塞 同步 异步的概念。简述其异同点。
①.同步和异步,指的是在调用方发起调用之后,是否在调用返回的时候就已经得到了结果。同步,在调用返回时,就得到了结果。异步,则没有。
②.阻塞与非阻塞,指的是在输入输出设备未就绪时,操作系统是否会挂起进程。阻塞的话,操作系统会等待,直到输入输出设备就绪。如果是非阻塞的话, 操作系统就不会等待,先去执行其他的进程。
(另一位博主的解释:原文)
IO操作分为两个步骤,1:程序发出IO请求,2:完成实际IO操作。
阻塞、非阻塞是针对第一步划分的,而同步、异步是针对第二部划分的。
阻塞/非阻塞:一个I/O请求,在线程中进行,当这个I/O请求没有数据或者没有有效数据拉来完成I/O操作,那么这个请求不会结束,而是等待,而这个等待就是阻塞,因为他在等待数据,导致其他I/O操作无法进行。但是怎么解决这个事情呢,java中NIO库用来解决这个问题,通过buffer,channel,selector使I/O请求迅速获得是否可以进行实际操作的信息,无论可不可以,如果数据没有准备好,那么就返回信息没有准备好。然后进行别的I/O操作。
同步/异步:同步,如果是应用程序本身去完成这个I/O操作,那么这个应用程序这个进程就会被阻塞,这里的阻塞和上面的阻塞不同,上面的阻塞和非阻塞其实都属于同步,因为无论他们在请求时是否阻塞线程,他们的I/O操作都是由应用程序进行的。而异步I/O就不同了,它将实际I/O操作交给操作系统去完成,首先应用程序本身的进程就没有阻塞,其次操作系统能利用的进程很多,等实际I/O操作完成之后,通知程序一声就可以了。
3.定义一个宏,清除整型x的某一bit位y.
#define check_bit(x,y) ((x)&(0x01<<(y))) //检测x的y位
#define set_bit(x,y) ((x)|=(0x01<<(y))) //置位x的y位
#define clr_bit(x,y) ((x)&=(~(0x01<<(y)))) //清零x的y位
int main() {
int x = 8; //00001000
//int y = 3; //清除右边数第4位。
//clr_bit(x, y);
int y = 2; //置位右边数第3位。置位1.
set_bit(x, y);
cout << x;
return 0;
}
4.一个关于函数指针和typedef结合的题目。该题的最终输出是240.
int add(int a) {
return (++a);
}
int multi(int* a, int* b, int* c) {
return(*c = *a * *b);
}
typedef int (*FUNC1)(int n); //FUNC1是一种数据类型。
//它定义出来的是指针。指向的是一种函数,这种函数形参是整数,返回值是整数。
typedef int (*FUNC2)(int*,int*,int*);
void show(FUNC2 func,int arg1,int* arg2) {
FUNC1 p = &add;
int temp = p(arg1);
func(&temp,&arg1,arg2);
printf("%d\n", *arg2);
}
int main() {
int a = 0;
show(&multi, 15, &a);
return 0;
}
5.判断单链表是否存在环,若有环,环的长度,环与链表的连接点,带环的整个链表的长度。
答:快慢指针判断是否存在环,这哥们甚至给出了证明!我觉得他的证明是对的。
如何判断,环与链表的连接点是哪一个节点?让慢指针从头结点开始遍历,直到第一次遍历到重复的节点为止。这个就是环与链表的连接点。接下来就可以求出,环的长度和整个链表的长度了。
6.考到了多少年,我都没看到过的Union的用法,这东西还真是神奇。
union {
int nVal;
char arrB[2];
}uTest;
//uTest 占用4个字节,只有int的空间,
//Union的类型的变量占用的空间的大小是最大的元素占用空间的大小。
int main() {
uTest.arrB[0] = 0x12;
uTest.arrB[1] = 0x34;
//这里虽然是给arrB这个数组赋值,但是此时arrB和nVal的内存是同一块内存
//即对任何一个的操作,都会对另一个造成影响。
printf("%#X",uTest.nVal); //输出是0X3412
return 0;
}
参考的这篇博客。
7.Linux由哪五个子系统组成,每个子系统的作用分别是什么?
答:分别是,进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。
进程调度,即选择最“合适”的进程去访问CPU.
内存管理,允许多个进程安全的共享主内存区域。
虚拟文件系统,隐层了不同的文件系统的具体细节,对外提供统一的接口。
网络接口,提供了对各种网络标准和各种网络硬件的支持。
进程间通信,支持进程间的各种通信机制。
8.仅用O(1)的空间,将整数数组按奇偶数分成2部分,数组左边是奇数、右边是偶数。请给出完整代码,尽量高效,简洁。
思路:定义两个变量a和b,分别指向数组的头和尾。a向后移动,遇到偶数,停下,b向前移动,遇到奇数停下,交换a和b的值,然后两者继续运动。直到a和b相遇。参考的这位大佬的博客
代码:
int main() {
int arr[10] = { 123,34,7,4,2,67,8,3,534564,677 };
int a = 0;
int b = sizeof(arr) / sizeof(int)-1;
while (a < b) { //a一定要小于b, 大于肯定是退出,等于的时候也退出,因为也不用再计算了。
while (arr[a] % 2 == 1 && a<b) { a++; } //如果是奇数,且比右边的b小,则向后移动。
while (arr[b] % 2 == 0 && b>a) { b--; } //如果是偶数,且比左边的a大,则向前移动。
int temp = arr[b];
arr[b] = arr[a];
arr[a] = temp;
}
for (auto i : arr) {
cout << i << " ";
}
return 0;
}
9.对原字符串可进行任意位置的插入、删除、替换三个操作,最少经过多少次操作,才能得到目标串。
这个问题是实际上求的是编辑距离。
代码:
int min(int a, int b) {
return (a < b) ? a : b;
}
int minOperationCount(string source, string target) {
int len1 = source.length(); //原字符串的长度。
int len2 = target.length(); //目标字符串的长度。
int** res = new int* [len1 + 1]; //动态定义一个二维数组
for (int i = 0; i < len1 + 1; ++i)
res[i] = new int[len2 + 1];
for (int i = 0; i < len1 + 1; ++i) //这个二维数组的边界,也就是动态规划的边界。
res[i][0] = i;
for (int j = 0; j < len2 + 1; ++j)
res[0][j] = j;
//这一串的边界,就是上面图中的前三种情况。
//那么,接下来就是第4种情况了。即i和j都>0的时候。
for (int i = 1; i < len1 + 1; ++i)
{
for (int j = 1; j < len2 + 1; ++j)
{
int tmp = min(res[i - 1][j] + 1, res[i][j - 1] + 1);
int d;
if (source[i - 1] == target[j - 1])
d = 0;
else
d = 1;
res[i][j] = min(tmp, res[i - 1][j - 1] + d);
}
}
int result = res[len1][len2];
for (int i = 0; i < len1 + 1; i++)
{
delete[] res[i];
res[i] = NULL;
}
delete[] res;
res = NULL;
return result;
}
int main() {
string a;
string b;
cin >> a;
cin >> b;
cout<<minOperationCount(a, b);
return 0;
}
竟然过了笔试,没想到,第一面和hr面的,发现自己越来越喜欢和hr面试了。哈哈哈。