1. 程式人生 > >騰訊2017秋招筆試題難點記錄

騰訊2017秋招筆試題難點記錄

1.在vs編譯環境下,以下程式碼的執行情況:

1234567int f(int a, int b, int c){return 0;}int main(){return  f(printf("a"),printf("b"),printf("c"));}
解析: 該題考察函式的引數傳遞: 當用函式做實參時,編譯器一般會根據引數傳遞順序,先計算出函式的返回值,然後將返回值傳遞給原來的函式。 在x86架構(Linux或Unix系統)上,函式的引數是通過棧傳遞的。因此引數從右往左入棧順序是:printf("c"),printf("b"),printf("a")。依次計算出結果:cba 在x86_64架構(Linux或Unix系統)上,函式的前6個引數是通過暫存器傳遞的,超出6個的部分(如第7個引數,第8個引數等等)通過棧傳遞。因此引數進暫存器順序是:printf("a"),printf("b"),printf("c")。依次計算出結果:abc

2.具有3個節點的二叉樹有幾種形態?

3.在Linux上,對於多程序,子程序繼承了父程序的下列哪些?

解析:

  • 子程序繼承父程序
    • 使用者號UIDs和使用者組號GIDs
    • 環境Environment
    • 堆疊
    • 共享記憶體
    • 開啟檔案的描述符
    • 執行時關閉(Close-on-exec)標誌
    • 訊號(Signal)控制設定
    • 程序組號
    • 當前工作目錄
    • 根目錄
    • 檔案方式建立遮蔽字
    • 資源限制
    • 控制終端
  • 子程序獨有

    • 程序號PID
    • 不同的父程序號
    • 自己的檔案描述符和目錄流的拷貝
    • 子程序不繼承父程序的程序正文(text),資料和其他鎖定記憶體(memory locks)
    • 不繼承非同步輸入和輸出
  • 父程序和子程序擁有獨立的地址空間和PID引數。子程序從父程序繼承了使用者號和使用者組號,使用者資訊,目錄資訊,環境(表),開啟的檔案描述符,堆疊,(共享)記憶體等。經過fork()以後,父程序和子程序擁有相同內容的程式碼段、資料段和使用者堆疊,就像父程序把自己克隆了一遍。事實上,父程序只複製了自己的PCB塊。而程式碼段,資料段和使用者堆疊記憶體空間並沒有複製一份,而是與子程序共享。只有當子程序在執行中出現寫操作時,才會產生中斷,併為子程序分配記憶體空間。由於父程序的PCB和子程序的一樣,所以在PCB中斷中所記錄的父程序佔有的資源,也是與子程序共享使用的。這裡的“共享”一詞意味著“競爭“。

  • 4.設有一個遞迴演算法如下
  • 1234int f(int n) {if(n<=3) return 1;else return f(n-2)+f(n-6)+1;}

    試問計算f(f(9))時需要計算()次f函式。

    解析: 一、先算內層f(9)     [1] 計算 f(9) = f(7) + f(3) + 1;     [2] 計算[1]中 f(7) = f(5) + f(1) + 1;     [3] 計算[2]中 f(5) = f(3) + f(-1) + 1;     [4] 計算[3]中 f(3) = 1;     [5] 計算[3]中 f(-1) = 1;         {至此f(5)可計算得: f(5) = 1 + 1 + 1 = 3}     [6] 計算(1)中f(1) = 1;         {至此f(7)可計算得 :f(7) = 3 + 1 + 1 = 5}     [7] 計算[1]中f(3) = 1;         {至此f(9)可計算得:f(9) = 5 + 1 + 1 = 7} 計算f(9)一共呼叫了7次函式 二、計算外層f(7)     由上面步驟可知,計算f(7)呼叫了5次函式 所以一共呼叫了函式7+5=12次 5.寢室有6個同學打dota,分為對立的兩方,一方是天災軍團,一方是近衛軍團。現請你設定賽程以及每場的對陣(每方最少1人、最多5人),請問至少得進行多少場比賽,才能使得賽程結束後每位同學都和其他同學做過對手()
  • 解析:用三位二進位制來表示某個人三場比賽各場比賽所在的一方,比如我們用0代表在天災,1代表在近衛,那麼000就代表這個人三場比賽都在天災,而001表示這個人前兩場比賽在天災,第三場比賽在近衛。那麼三位二進位制可以有8種表示,而每一種表示都與其他7種的表示至少在一個位置上的數字是不一樣的,所以最多8人至少三場可以每個人都做過對手。
  • 6.

    以下程式碼列印的結果是(假設執行在 64 位計算機上):

    12345678struct st_t {int status;short *pdata;char errstr[32]; };st_t st[16];char *p=(char *)(st[2].esstr+32);printf(“%d”,(p-(char *)(st)));
    解析: 根據位元組對齊,在64位系統下structst_t 結構體佔用的位元組為48個。 struct st_t { int status;  //佔用8個(後面的4個為對齊位) short *pdata;//佔用8個 char errstr[32];//佔用32個 }; char*p=(char*)(st[2].esstr+32),p實際指向了st[3] p-(char*)(st)),即為&st[3]-&st[0],佔用空間為3個結構體的大小,即3*48=144
  • 7. 請選擇下列程式的輸出結果是()
    1234567891011121314#include <stdio.h>int main(){const int N=10;const int M=2;int* a=new int[N];for(int i=0;i<N;++i)a[i]=(0==i%2)?(i+2):(i+0);int (*b)[N/M]=(int (*)[N/M])a;for(int i=0;i<M;++i)for(int j=0;j<N/M;++j)printf(“%d”,b[i][j]);return 0;}
    解析:int a[] = {2,1,4,3,6,5,8,7,10,9};  //一維陣列a           int (*b)[N/M]=(int (*)[N/M])a;    //將一維陣列a強制轉化為陣列指標並賦值給陣列指標b;   //上面兩句可以拆分為以下幾句理解;          int a[2][N/M] = {2,1,4,3,6,5,8,7,10,9};          int (*b)[N/M];   //b是陣列指標,指向具有N/M個元素的一維陣列;         b = a; 8.
  • 關於c++中的虛擬函式和解構函式還有建構函式的關係?
  • C++中 的虛擬函式的作用主要是實現了多型的機制。而虛擬函式是通過虛擬函式表(V-Table)實現的。 建構函式不能宣告為虛擬函式,解構函式可以宣告為虛擬函式,而且有時是必須宣告為虛擬函式。 建構函式為什麼不能宣告為虛擬函式? 1 構造一個物件的時候,必須知道物件的實際型別,而虛擬函式行為是在執行期間確定實際型別的。而在構造一個物件時,由於物件還未構造成功。編譯器無法知道物件的實際型別,是該類本身,還是該類的一個派生類,或是更深層次的派生類。無法確定。  2 虛擬函式的執行依賴於虛擬函式表。而虛擬函式表在建構函式中進行初始化工作,即初始化vptr,讓他指向正確的虛擬函式表。而在構造物件期間,虛擬函式表還沒有被初 始化,將無法進行。 解構函式執行時先呼叫派生類的解構函式,其次才呼叫基類的解構函式。 解構函式為什麼宣告為虛擬函式? 如果解構函式不是虛擬函式,而程式執行時又要通過基類的指標去銷燬派生類的動態物件,那麼用delete銷燬物件時,只調用了基類的解構函式,未呼叫派生類的解構函式。這樣會造成銷燬物件不完全。 包含至少一個純虛擬函式的類視為抽象類
  • 9.值型別與引用型別區別:
  • 值型別

    引用型別

    儲存方式

    直接儲存資料本身

    儲存的是資料的引用,資料儲存在資料堆中

    記憶體分配

    分配在棧中的

    分配在堆中

    效率

    效率高,不需要地址轉換

    效率較低,需要進行地址轉換

    記憶體回收

    使用完後立即回收

    使用完後不立即回收,而是交給GC處理回收

    賦值操作

    建立一個新物件

    建立一個引用

    型別擴充套件

    不易擴充套件,所有值型別都是密封(seal)的,所以無法派生出新的值型別

    具有多型的特性方便擴充套件

    例項分配

    通常是線上程棧上分配的(靜態分配),但是在某些情形下可以儲存在堆中

    總是在程序堆中分配(動態分配)

    10.
  • 下面程式碼的執行結果是()
    123456789int main(void){char *p[]={“TENCENT”,”CAMPUS”,”RECRUITING”};char **pp[]={p+2,p+1,p};char ***ppp=pp;printf(“%s”,**++ppp);printf(“%s”,*++*++ppp);return 0;}
    解析:
  • 從題幹當中,我們可以畫出這樣的一個圖,這樣就比較直觀的看出了p,pp,ppp都指向哪裡了,關鍵是最後兩個printf語句。
    (1)printf(“%s”,**++ppp);即,ppp當前所指向的位置,再往下移一個位置,即pp的位置2,而pp的位置2指向的是p的位置2,p的位置2指向的是CAMPUS,所以先輸出CAMPUS (2)printf(“%s”,*++*++ppp);這個語句等價於 printf(“%s”,*++(*++ppp));所以我們首先看,++ppp,第一個printf語句中ppp已經指向了pp的位置2,所以再往下移一個,指向了pp的位置3,而(*++ppp)則代表pp位置3所指向的內容,即p的位置1(pp的位置3指向的是p的位置1),在此基礎上前面再加上一個++,則代表指標p在位置1的基礎上再往下移動,即指標p的位置2,而p的位置2所指向的內容是CAMPUS,所以第二行輸出的也是CAMPUS。
    所以正確答案是:CAMPUS CAMPUS