筆試
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面的,發現自己越來越喜歡和hr面試了。哈哈哈。
涼了。