真題筆試解題報告


2017阿里實習生筆試題(二)

下面哪一個不是動態鏈接庫的優點?

A.共享
B.裝載速度快
C.開發模式好
D.減少頁面交換


靜態鏈接與動態鏈接:

1 靜態鏈接庫的優點 

 (1) 代碼裝載速度快,執行速度略比動態鏈接庫快; 

 (2) 只需保證在開發者的計算機中有正確的.LIB文件,在以二進制形式發布程序時不需考慮在用戶的計算機上.LIB文件是否存在及版本問題,可避免DLL地獄等問題。 

2 動態鏈接庫的優點 

 (1) 更加節省內存並減少頁面交換(靜態庫把所有的文件都裝入了,而動態庫只是裝入了部分需要的);

 (2) DLL文件與EXE文件獨立,只要輸出接口不變(即名稱、參數、返回值類型和調用約定不變),更換DLL文件不會對EXE文件造成任何影響,因而極大地提高了可維護性和可擴展性;

 (3) 不同編程語言編寫的程序只要按照函數調用約定就可以調用同一個DLL函數;

 (4)適用於大規模的軟件開發,使開發過程獨立、耦合度小,便於不同開發者和開發組織之間進行開發和測試。

3 不足之處

 (1) 使用靜態鏈接生成的可執行文件體積較大,包含相同的公共代碼,造成浪費;

 (2) 使用動態鏈接庫的應用程序不是自完備的,它依賴的DLL模塊也要存在,如果使用載入時動態鏈接,程序啟動時發現DLL不存在,系統將終止程序並給出錯誤信 息。而使用運行時動態鏈接,系統不會終止,但由於DLL中的導出函數不可用,程序會加載失敗;速度比靜態鏈接慢。當某個模塊更新后,如果新模塊與舊的模塊 不兼容,那么那些需要該模塊才能運行的軟件,統統撕掉。這在早期Windows中很常見。


由權值分別為1、12、13、4、8的葉子節點生成一顆哈夫曼樹,它的帶權路徑長度為()




哈弗曼樹:哈夫曼樹是一種帶權路徑長度最短的二叉樹,也稱為最優二叉樹。
哈夫曼編碼:利用哈夫曼樹求得的用於通信的二進制編碼稱為哈夫曼編碼。樹中從根到每個葉子節點都有一條路徑,對路徑上的各分支約定指向左子樹的分支表示”0”碼,指向右子樹的分支表示“1”碼,取每條路徑上的“0”或“1”的序列作為各個葉子節點對應的字符編碼,即是哈夫曼編碼。
ds50

就拿上圖例子來說:

A,B,C,D對應的哈夫曼編碼分別為:111,10,110,0


 

已知IBM的PowerPC是big-endian字節序列而Intel的X86是little-endian字節序,如果在地址啊存儲的整形值時0x04030201,那么地址為a+3的字節內存儲的值在PowerPC和Intel X86結構下的值分別是?
 
大端從大地址開始存儲,小端相反,兩者都是從數據低位開始存起;
假設從上至下地址遞增,則
PowerPC(大):                    Intel X86(小):
04                                            01                    低
03                                            02                      |
02                                            03                      | 
01                                            04                     高

 a+3指向最大的地址,所以分別為1 4


 

在TCP/IP建立連接過程中,客戶端或服務器的狀態轉移說法錯誤的是?
經歷SYN_RECV狀態
經歷SYN_SEND狀態
經歷ESTABLISHED狀態
經歷TIME_WAIT狀態
服務器在收到syn包時將加入半連接隊列
服務器收到客戶端的ack包后將從半連接隊列刪除



Tcp/Ip有3次握手:
第一次握手:客戶端向服務器端發送SYN包(syn=j),進入 SYN_SEND狀態,等待服務器確認。
第二次握手:服務器收到 SYN包,確認SYN,此時syn=j+1,同時發送一個SYN包(syn=k)即SYN+ACK包,此時服務器進入 SYN_RECV狀態;
第三次握手: 客戶端收到SYN+ACK包,向服務器發送ACK確認包,此時客戶端和服務器端均進入 ESTABLISHED狀態。
 
其中有一個 半連接狀態:服務器維護一個半連接隊列(可以利用半連接進行ddos syn泛洪攻擊),該隊列衛每個客戶端SYN包開設一個條目,標明服務器已經接到SYN包,並向客戶端發出確認,這些條目表示的連接處於SYN_RECV狀態, 得到客戶端的確認后進入ESTABLISHED狀態。
TIME_WAIT是斷開連接時的狀態

問題描述: 
#pragma pack(2)
class BU
{
    int number;
    union UBffer
    {
        char buffer[13];
        int number;
    }ubuf;
    void foo(){}
    typedef char*(*f)(void*);
    enum{hdd,ssd,blueray}disk;
}bu;

 

sizeof(bu)的值是()

第一行的使得對齊的補齊字節為2。

因此union的大小為13+1=14.

void foo(){} 不占   ——函數不占用sizeof的內存。

typedef char*(*f)(void*); 不占——聲明不占用內存

 enum{hdd,ssd,blueray}disk; 4個字節—— -- 實質上就是整型,所以 size 是 4

 


在動態分區分配方案中,系統回收主存,合並空閑空間時需修改空閑區表,以下哪種情況空閑區會減1?  

A.只要回收主存,空閑區數就會減一
B.空閑區數和主存回收無關
C.無上鄰空閑區,也無下鄰空閑區
D.有上鄰空閑區,但無下鄰空閑區
E.有下鄰空閑區,但無上鄰空閑區
F.有上鄰空閑區,也有下鄰空閑區
 
          
A:錯誤,因為回收主存時,要根據相鄰分區空閑情況決定空閑分區個數,如果不考慮合並的話,空閑分區個數增加一個,因為可能發生合並情況,所以可能- ,可能不變;
B:由上知,錯誤;
C:無上鄰空閑區,也無下鄰空閑區,不需要合並空閑分區,空閑分區個數加1;
D:有上鄰空閑區,但無下鄰空閑區,需要將剛剛的空閑分區表的起始地址修改為上鄰空閑區的起始地址和空閑分區大小,但是空閑分區個數不變;
E:有下鄰空閑區,但無上鄰空閑區,剛剛的空閑分區表起始位置不用改變,空閑分區大小改變,空閑分區個數不變;
F:有上鄰空閑區,也有下鄰空閑區,假設原來是2個空閑分區,新回收一個,發現前后都是空閑的,將三個合並為1個,最后結果為1個空閑分區,空閑分數個數減1

int* pint = 0; 
pint += 6; 
cout << pint << endl;

 

以上程序的運行結果是: 24


首先如果直接cout一個指針,打印的是其所村春的變量的地址。
這個指針+6,一個指針的大小是4,加4*6就是24.


下面哪種協議在數據鏈路層?

ARP
ICMP
FTP
UDP
HTTP
VPN

ICMP是網絡層,UDP是傳輸層,FTP和HTTP是應用層

目前VPN隧道協議主要有4種:點到點隧道協議PPTP、第二層隧道協議L2TP、網絡層隧道協議IPSec以及SOCKS v5協議。其中,PPTP和L2TP工作在數據鏈路層,IPSec工作在網絡層,SOCK v5工作在會話層。

TCP/IP模型中,ARP協議屬於網絡層,在OSI參考模型中,ARP屬於數據鏈路層

 


 

以下哪種方式,在讀取磁盤上多個順序數據塊時的效率最高?
中斷控制方式
DMA方式
通道方式
程序直接訪問方式
循環檢查I/O方式
以上訪問方式都一樣


答案選C,通道方式。
(1)程序直接訪問方式跟循環檢測IO方式:CPU和IO串行,每讀一個字節(或字),CPU都需要不斷檢測狀態寄存 器的busy標志,當busy=1時,表示IO還沒完成;當busy=0時,表示IO完成。此時讀取一個字的過程才結束,接着讀取下一個字。
(2)中斷控制方式:循環檢測先進些,IO設備和CPU可以並行工作,只有在開始IO和結束IO時,才需要CPU。但每次只能讀取一個字。
(3)DMA方式:Direct Memory Access,直接存儲器訪問,比中斷先進的地方是每次可以讀取一個塊,而不是一個字。 在中斷驅動方式中,I/O設備與內存之間的數據交換必須經過CPU中的寄存器,所以速度還是受限,而DMA方式的基本思想是在I/O設備和內存之間開辟直接的數據交換通路。
(4)通道方式:I/O通道控制方式是DMA控制方式的發展,是以一組數據塊為單位的,即可以連續讀取多個數據塊(每次可以處理多個塊,而不只是一個塊)

C/C++工程師能力評估

以下prim函數的功能是分解質因數。括號內的內容應該為?
void prim(int m, int n)
 {
     if (m >= n)
     {
         while (        ) n++;
         (     );
         prim(m, n);
         cout << n << endl;
     }
 }
m/=n   m%=n

分解質因數:從n=2開始,找到第一個m的質因子,因此需要m&n==0,然后繼續找剩下的質因子,此時m=m/n。也可以用循環法做,此處不再贅述。
補充一下判斷是否為質數的代碼:
int isPrime(int n)  
{  
    int flag = 1;  
    for(int i = 2; i*i <= n; i++)  
        if(n%i == 0)  
            flag = 0;  
    return flag;  
}  

補充一下最大公約數的代碼:(輾轉相除法)

 

int gcd(int x,int y) //用輾轉相除法,求兩數的最大公約數  
{  
    int r;  
    while(y>0)  
    {  
        r=x%y;  
        x=y;  
        y=r;  
    }  
    return x;  
}

求兩數的最大公約數,可采用歐幾里得方法:只要兩數不相等,就反復用大數減小數,直到相等為止,此相等的數就是兩數的最大公約數。

int gcd(int x,int y)
{
    while(x!=y)
    {
        if(x>y) x=x-y;
        else y=y-x;
    }
    return x;
}

補充一下最小公倍數的代碼:先求出最大公約數,用x*y/gcd(x,y)就可以得到最小公倍數。

 


 

enum string{    
    x1,    
    x2,    
    x3=10,    
    x4,    
    x5,    
} x;

函數外部問x等於什么?0

如果是函數外定義那么是0
如果是函數內定義,那么是隨機值,因為沒有初始化;

  • 數組在當做參數傳給函數的時候,會被退化為普通指針,因此,sizeof還是4字節。
  • 指針在做加減的時候,是根據其所指的類型進行加減的,因此加減sizeof(type)。
  • 靜態局部變量要比全局變量先銷毀。

 

  • 重載和重寫的一些要求:
方法重載(overload):
1.必須是同一個類
2方法名(也可以叫函數)一樣
3參數類型不一樣或參數數量不一樣

方法的重寫(override)兩同兩小一大原則:
方法名相同,參數類型相同
子類返回類型小於等於父類方法返回類型,
子類拋出異常小於等於父類方法拋出異常,
子類訪問權限大於等於父類方法訪問權限。
 
  • char(* ss)[N]表示是一個指向一個數組(字符串數組),這個數組存的類型是——char[N],即又是一個一位數組。
  • 類的大小注意點:
    1 先找有沒有virtual 有的話就要建立虛函數表,+4
    2 static的成員變量屬於類域,不算入對象中,成員函數也不占空間      +0
    3 神馬成員都沒有的類,或者只有成員函數        +1
    4 對齊法則,對大家都沒有問題,看看有沒有被#pragma pack(n)設置大小

int main()
{
    MyClass obj1(1), obj2(2);
    MyClass obj3 = obj1;
    return 0;
}

關於拷貝構造和賦值的問題:以上例子中obj3調用的是拷貝構造函數,因為obj3還未存在,需要調用拷貝構造函數。

假如obj3已經存在,才會調用賦值函數。

 


 數組全排列:

 

算法思路:

 

(1)n個元素的全排列=(n-1個元素的全排列)+(另一個元素作為前綴);

 

(2)出口:如果只有一個元素的全排列,則說明已經排完,則輸出數組;

 

(3)不斷將每個元素放作第一個元素,然后將這個元素作為前綴,並將其余元素繼續全排列,等到出口,出口出去后還需要還原數組

 

void perm(int list[], int k, int m)
{
    if (k==m)
    {
        copy(list,list+m,ostream_iterator<int>(cout," "));
        cout<<endl;
        return;
    }
    for (int i=k; i<=m; i++)
    {
        swap(&list[k],&list[i]);
        perm(list,k+1,m);
        swap(&list[k],&list[i]);
    }
}

騰訊2016研發工程師在線模擬筆試題 

1、 32位系統中,定義**a[3][4],則變量占用內存空間為()。
解析:**a[3][4]表示a存儲的地址,指向一個大小為3*4的數組,這個數組里存儲的是指針。指針大小為4,4*3*4=48。

2、有36輛自動賽車和6條跑道,沒有計時器的前提下,最少用幾次比賽可以篩選出最快的三輛賽車?

解析:6次得到小組第一,a1 b1 c1 d1 e1 f1,def所在的小組的二三名舍棄。
第七次,a1,b1,c1,d1,e1,f1進行比賽。得到前三,假設為b1,d1,e1。則a1,c1,f1被舍棄。
第八次,在b2,b3,d1,d2,e1種得到第二名和第三名。

3、下列哪些http方法對於服務端和用戶端一定是安全的?()

解析:

Get:通過請求URI得到資源。
Post:不僅可以查詢也可以添加新的內容,請求放在Body里。
Head:類似於Get,但是不返回body,獲得請求uri的響應報頭,用於檢查對象是否存在。
Trace:用於遠程診斷服務器。
Options:列出請求的資源支持的所有方法。
Head、Get、Options和Trace視為安全的方法,因為它們只是從服務器獲得資源,而不對服務器做任何修改,但對於用戶端未必安全。例如get容易將敏感信息暴露在uri中。

4、一個系統,提供多個http協議的接口,返回的結果Y有json格式和jsonp格式。Json的格式為 {"code":100,"msg":"aaa"},為了保證該協議變更之后更好的應用到多個接口,為了保證修改協議不影響到原先邏輯的代碼,以下哪些設 計模式是需要的?協議的變更指的是日后可能返回xml格式,或者是根據需求統一對返回的消息進行過濾。()

解析:裝飾者模式:動態添加功能,例如過濾功能,並不修改原來的邏輯。
proxy模式:為類提供代理,使得代理類可以控制對 對象的訪問,例如實現延遲實例化,控制訪問等。
composite模式:組合模式,將對象組合成樹形結構以表示"部分-整體"的層次結構。使得用戶對單個對象和組合對象的使用具有一致性。

5、對於定義"int *p",下列哪些說明可能是正確的?()

int (*p)「10」才表示一個指向一位數組的指針。
int * (*p)「10」表示一個指向二位數組的指針。
int arr「10」;
int *p=arr;此時p保存的是第一個元素的地址。

6、十字鏈表是無向圖的一種存儲結構()是

解析:

圖的存儲結構:

鄰接矩陣,也成為數組表示法。用一個一維數組存儲圖中頂點的信息,用一個二維數組存儲邊的信息。

鄰接表:是一種順序存儲和鏈式存儲結合的存儲方法。

十字鏈表:即把每一條邊的邊結點分別組織到以弧尾頂點為頭結點的鏈表和以弧頭頂點為頭頂點的鏈表中。在十字鏈表表示中,頂點表和邊表的結點結構分別如圖8.13 的(a)和(b)所示。

http://c.biancheng.net/cpp/uploads/allimg/120223/1-120223224210X3.jpg

http://c.biancheng.net/cpp/uploads/allimg/120223/1-120223224315394.jpg

7、在一個元素個數為N的數組里,找到升序排在N/5位置的元素的最優算法時間復雜度是 O(n)

解析:算法課上講過的,線性復雜度的找第K大的元素的算法。

BFPRT算法解決的問題十分經典,即從某n個元素的序列中選出第k大(第k小)的元素,通過巧妙的分 析,BFPRT可以保證在最壞情況下仍為線性時間復雜度。

該算法的思想與快速排序思想相似,當然,為使得算法在最壞情況下,依然能達到o(n)的時間復雜 度,五位算法作者做了精妙的處理。

算法步驟:

1. 將n個元素每5個一組,分成n/5(上界)組。

2. 取出每一組的中位數,任意排序方法,比如插入排序。

3. 遞歸的調用selection算法查找上一步中所有中位數的中位數,設為x,偶數個中位數的情況下設定為選取中間小的一個。

4. 用x來分割數組,設小於等於x的個數為k,大於x的個數即為n-k。

5.  若i==k,返回x;(i就是中位數)

  若i<k,在小於x的元素中遞歸查找第i小的元素;

  若i>k,在大於x的元素中遞歸查找第i-k小的元素。

  終止條件:n=1時,返回的即是i小元素。

8、下面的程序輸出可能是什么?一直是“ab”

class Printer{
    public:
        Printer(std::string name) {std::cout << name;}
};
class Container{
    public:
        Container() : b("b"), a("a") {}
    Printer a;
    Printer b;
};
int main(){
    Container c;
    return 0;
}

 

 初始化列表的初始化順序  與在列表中的順序無關,由變量在類中定義的先后順序決定.

因此先初始化a,然后再初始化b。

還有一種題目:

    class A  
    {  
    private:  
        int n1;  
        int n2;  
          
    public:  
        A():n2(0),n1(n2+2){}  
      
        void Print(){  
            cout << "n1:" << n1 << ", n2: " << n2 <<endl;    
        }  
    };  
      
    int main()  
    {  
      
        A a;  
        a.Print();  
      
        return 1;  
    }  

 

 這個題目,因為先初始化n1,此時n2還未初始化,所以是未定義值。后來n2才被初始化為0.因此結果是下圖:

9、下面程序段包含4個函數,其中具有隱含this指針的是() f4

int f1();
class T
{
    public:static int f2();
    private:friend int f3();
    protect:int f4();
};

解析:

靜態成員函數屬於整個類所擁有,沒有this指針
友員函數不是這個類的成員,沒有
類的非靜態成員函數  有
補充:之前有個問題,能不能同時用const和static修飾類的成員函數。答案是不能,為什么?因為const函數(成員函數)會隱含const this*以保證不修改類的成員。而static不能有this指針。因此不能同時修飾。

 10、如果在一個排序算法的執行過程中,沒有一對元素被比較過兩次或以上,則稱該排序算法為節儉排序算法,以下算法中是節儉排序算法的有________。插入排序,歸並排序

解析:

A。插入排序的思想是對第i+1位置上的數,將其插入前i個有序數組中。插入以后形成新的有序數組,根據排序數組不會在比較的原則,該元素不可能再次比較了。

B。選擇排序的思想是對當前第i個位置上的數,那么在后續數組中,選最小的與i對換。說明肯定比較過第二小和第三的數。那么在i+1位置上,上次第二小和第三小的數還需要比較一次選出最小的與i+1交換。那么至少比較了兩次。
C。堆排序。堆排序分兩步。初始建堆和堆重建。當最大元素與最末尾元素交換后。面臨堆重建的問題。那么堆頂元素下層過程中,必然與第二小的元素比較一次。再一次堆重建,假設第二小元素被替換的時候,他們會在比較一次。
D。歸並排序思路是對兩個已經排好序的數組,同時向后移動。那么每個元素只會與其他數組中的元素比較一次。然后合並在一起。根據同組元素不會比較的原則的,以后兩個元素不可能在比較到。
11、一棵哈夫曼樹的帶權路徑長度等於其中所有分支結點的權值之和()錯
解析:哈弗曼樹又稱最優樹,
 
路徑長:第L層的路經長為L-1
帶權路徑長度(Weighted Path Length of Tree):定義為樹中所有葉結點的帶權路徑長度之和。
結點的帶權路徑長度:結點到樹根之間的路徑長度與該結點上權的乘積。
————————————————————————————————

哈夫曼編碼:(左1右0/左0右1)。得到碼表,根據出現的概率乘以碼長可以得到平均碼長。平均碼長/等長編碼=壓縮率。

哈夫曼編碼步驟:(找最小權值的節點拿出來構成一顆二叉樹,生成一個中間節點,再重復)

一、對給定的n個權值{W1,W2,W3,...,Wi,...,Wn}構成n棵二叉樹的初始集合F= {T1,T2,T3,...,Ti,...,Tn},其中每棵二叉樹Ti中只有一個權值為Wi的根結點,它的左右子樹均為空。(為方便在計算機上實現算 法,一般還要求以Ti的權值Wi的升序排列。)
二、在F中選取兩棵根結點權值最小的樹作為新構造的二叉樹的左右子樹,新二叉樹的根結點的權值為其左右子樹的根結點的權值之和。
三、從F中刪除這兩棵樹,並把這棵新的二叉樹同樣以升序排列加入到集合F中。
四、重復二和三兩步,直到集合F中只有一棵二叉樹為止。

12、若以下程序

#include <stdio.h>
main()
{ 
    FILE *fp;
    int i,a[ 6]={1,2,3,4,5,6},k;
    fp = fopen ("data.dat", "w+b");
    for (i=0;i<6;i+ +)
    { 
        fseek(fp,0L,0);
        fwrite(&a[5—i],sizeof(int),1,fp);
    }
    rewind(fp);
    fread(&k,sizeof(int),1,fp);
    fclose(fp);
    printf("%d",k);
}

 

解析:

C的關於文件I/O的函數庫,首先定義了一個文件流fp。然后以w+b的方式打開data文件。下圖是open的mode選項:r讀w寫a追加  加b表示binary二進制模式
然后fseek 用於二進制方式打開的文件,移動文件讀寫指針位置。最后開頭只有1。將文件內部的位置指針重新指向一個流(數據流/文件)的開頭。654321,每次都寫開頭,都覆蓋模式。 rewind(fp);也是將讀寫指針移到開頭。最后讀入k只有1.

 

13.對{05,46,13,55,94,17,42}進行基數排序,一趟排序的結果是:()42,13,94,05,55,46,17

解析:

算法導論中原話:“ 基數排序是首先按最低位有效數字進行排序,才是穩定的算法”。有悖於比較兩個數的常理。
理由:基數排序,又稱為bucket sort,因為Hollerith當時的人們是拿着真的桶進行排序的。如果數字從高位開始排序,意味着有n位,就得有10^n個桶。因為你先排最高位,然 后對於這個最高位又要分出十個桶排下一位。比如現在有13,23,12,22,11這五個數。你先為高位排序,就相當於把十位為1的分在一個桶1里 (13,12,11),十位為2的分在一個桶2(22,23)里。然后在桶1和桶2之中剩下的元素排序((11),(12),(13))和((22), (23))。這樣如果有很多位數,桶就很多。但是從最低位開始排就只需要10個桶,每移動一位,就用針對那一位排序(把元素扔進桶)
14、下列關於 clone 和 fork 的區別描述正確的有?
clone是fork的升級版本,不僅可以創建進程或者線程,還可以指定創建新的命名空間(namespace)、有選擇的繼承父進程的內存、甚至可以將創建出來的進程變成父進程的兄弟進程等等
解析:
fork()是全部復制
vfork()是共享內存
clone()是可以將父進程資源有選擇地復制給子進程,而沒有復制的數據結構則通過指針的復制讓子進程共享,具體要復制哪些資源給子進程,由參數列表中的clone_flags來決定。另外,clone()返回的是子進程的pid。

14、下列關於bool,int,float,指針類型的變量a 與“零”的比較語句正確的有?

bool : if(!a)

int : if(a == 0)

float: if(a == 0.0) 錯誤:

指針: if(a == NULL)

解析:
由於計算機二進制表示浮點數有精度的問題,0.0(浮點double)實際上不是0,而是非常接近零的小數,所以C錯!
在ANSIC C中定義了FLT_EPSILON/DBL_EPSILON/LDBL_EPSILON來用於浮點數與零的比較,一般 if(fabs(a)<FLT_EPSILON)或if(fabs(a)< DBL_EPSILON)就可以表示a是否“為0”。而if(a==0.0)是永遠不會成立的,達不到要求!

 

 





















 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM