C/C++面試(一)

1、

int main(void)
{
printf("%s , %5.3s\n","computer","computer");
return 0;
}

輸出

computer , com
%m.ns 輸出佔m列,但只取字符串中左端n個字符。這n個字符輸出在m列的右側,左補空格。

2、
虛函數動態綁定
動態聯編就是程序在運行的時候知道該調用哪個函數,而不是編譯階段,所以這個機制應該是由虛函數支持的,即運行時的多態,基類的某個成員函數聲明爲虛函數,派生類繼承,而且同樣重寫該函數,那麼當聲明一個派生類的指針或者引用時,它所調用的函數是由該指針指向的對象確定的,這就是動態聯編

3、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class parent  
{  
    public:  
    virtual void output();  
};  
void parent::output()  
{  
    printf("parent!");  
}  
       
class son : public parent  
{  
    public:  
    virtual void output();  
};  
void son::output()  
{  
    printf("son!");  
}
1
2
3
4
son s; 
memset(&s , 0 , sizeof(s)); 
parent& p = s; 
p.output(); 
 執行結果是()
沒有輸出結果,程序運行出錯
memset 將s所指向的某一塊內存中的每個字節的內容全部設置爲ch指定的ASCII值, 
本代碼中, 不虛函數鏈表地址也清空了, 所以p.output調用失敗。 output函數的地址編程0

4、
#pragma pack(2)
class BU
{
    int number; // 4
    union UBffer
    {
        char buffer[13]; // 13
        int number; // 4
    }ubuf; // union的大小取決於它所有的成員中,佔用空間最大的一個成員的大小,並且需要內存對齊,這裏因爲#pragma pack(2),所以union的大小爲14,如果不寫#pragma pack(2),那麼union大小爲16【因爲與sizeof(int)=4對齊】
    void foo(){} //0
    typedef char*(*f)(void*); //0
    enum{hdd,ssd,blueray}disk; // 4
}bu;
因此sizeof(union) = 4+14 +0 +0 +4 = 22

C++ 類佔用空間計算方式

1、一個類佔用的空間主要是屬性佔用空間,而成員函數一般不佔用空間,但是虛函數佔用空間,需要說明的是,無論多少個虛函數,只要佔用4個字節即可,也就是索引指向一個虛擬表的首位置。另外需要說明的是佔用空間都考慮了對齊,所以不足4個的按照滿4個的算。


2、類的繼承,子類佔用空間是父類基礎上增加本類空間即可。所以說可以認爲,子類就是直接拷貝了父類的內容,然後結合自身的內容。而且存儲空間也是這個順序,即先父類分配空間,然後纔是子類空間。


3、靜態成員變量不佔用類空間,應該是確實沒有放入這個類的裏面,而且沒有指針指向它,只能通過類::來訪問,也就是說靜態成員是隨着類的存在而存在,而 不依賴於對象,它的存在意義主要還是區分,否則如何確定其意義,這還是體現了相關的都方一起的思想,比全局變量或者常量更方便使用和理解。


4、需要說明的是,虛函數對應的虛擬表在空間的其他位置,和對象是沒有聯繫的,但是虛擬表地址是和類統一的,也就是說一旦確定,無論在哪個對象中,其指針 值是一樣的,即虛擬表位置是一定的。指針放在對象的最前面,首先是指向虛函數的虛擬表指針,然後纔是其他成員變量空間。

5、
我認爲淺拷貝是一個不喜歡思考的懶漢,而深拷貝則是一個思維嚴謹,喜歡思考的人。對於懶漢來說,雖然給了他任務,但是他總是想盡量的少做一些事情,所以很多時候做出來的東西就是只看到了表面,不會去思考對不對。

struct X { int x; int y; }; 

對於懶漢來說,他很直白的看到了x,看到了y,然後就拷貝x和y,然後就不管了,反正我完成我的拷貝了,至於對不對,我不管。

而一旦有了引用或者指針,事情就不一樣了
struct X { int x; int y; int* p; }; 
懶漢依然只是直接表面級別的拷貝,於是拷貝x, y , p,但是他沒有思考接下來的事情對不對。對於指針或者引用來說,若是隻是拷貝表面,那麼拷貝後的物體的指針也和原來的指針指向的是同一個對象,所以雖然目的想完成一個完美的克隆體,但是卻發現克隆體和原來的物體中間還有一根線連着,沒有完美的分離。

int *p = new int(47); int *q = p; 

如q與p都是指向一個物體一樣。

那麼如果原來的物體銷燬了,但是現在拷貝的物體還在,那麼這時候你拷貝後的物體的成員指針就是一個懸掛指針,指向了不再存在的物體,那麼你訪問的話,那就不知道會發生什麼了。

而對於深拷貝,這一個勤奮的人,他不會只做表面,他會把每一個細節都照顧好。於是,當他遇到指針的時候,他會知道new出來一塊新的內存,然後把原來指針指向的值拿過來,這樣纔是真正的完成了克隆體和原來的物體的完美分離,如果物體比作人的話,那麼原來的人的每一根毛細血管都被完美的拷貝了過來,而絕非只是表面。所以,這樣的代價會比淺拷貝耗費的精力更大,付出的努力更多,但是是值得的。當原來的物體銷燬後,克隆體也可以活的很好。

然而事實上是這個世界上大多都是懶漢,包括編程的人,編譯器等,所以默認的行爲都是淺拷貝,於是有時候你需要做一個勤奮的人,讓事情做正確,自己去完成深拷貝所需要的事情。

6、
64位操作系統,不同類型變量對應的字節數爲:(紅色的表示與32位系統不同之處)
        char :1個字節         char*(即指針變量): 8個字節        short int : 2個字節         int:  4個字節         unsigned int : 4個字節         float:  4個字節         double:    8個字節         long:   8個字節        long long:  8個字節         unsigned long:  8個字節 
64位系統在內存管理方面遵循8字節對齊,原則:在8字節對齊的情況下,按8個字節爲單位分配存儲空間,如果不足,會自動補充,本次分配不足以存放下面的變量時,會重新分配空間。
7、
如果 const 位於 * 的左側,則 const 就是用來修飾指針所指向的變量,即指針指向爲常量; 
如果 const 位於 * 的右側, const 就是修飾指針本身,即指針本身是常量。

8、
虛函數可以在子類中進行重載,也可以不重載而沿用父類中的方法。但純虛函數必須重載,因爲在其聲明類中沒有函數實現。vritual void func()=0;
包含純虛函數的類爲抽象類,抽象類不能聲明對象,只能作爲基類
9、
浮點數判斷是否爲0

float : const EXPRESSION EXP = 0.000001

if ( a < EXP && a >-EXP)

10.

32位系統中,定義**a[3][4],則變量佔用內存空間爲 48bytes

**a[3][4]存儲的是指向地址的二維指針數組,32位系統一個地址4個字節,12*4=48byte

11.

一個無向圖 G=(V,E) 是連通的,那麼邊的數目大於等於頂點的數目減一:|E|>=|V|-1,而反之不成立。
如果 G=(V,E) 是有向圖,那麼它是強連通圖的必要條件是邊的數目大於等於頂點的數目:|E|>=|V|,而反之不成立。
沒有迴路的無向圖是連通的當且僅當它是樹,即等價於:|E|=|V|-1。
12.

在VC6.0中,stack的大小一般爲2M,不會自動增加。stack一般存放函數的參數列表、返回值、局部變量。

stack 不存儲全局變量的值的值

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

HEAD,GET,OPTIONS和TRACE視爲安全的方法,因爲它們只是從服務器獲得資源而不對服務器做任何修改,但是HEAD,GET,OPTIONS在用戶端不安全。而POST,PUT,DELETE和PATCH則影響服務器上的資源。
TRACE: 這個方法用於返回到達最後服務器的請求的報文,這個方法對服務器和客戶端都沒有什麼危險。OPTIONS:列出請求的資源所支持的所有方法。

14、下面函數的時間複雜度是

long foo(long x){

    if(x<2) return 1;

        return x*x*foo(x-1);

}

將這個遞歸轉換成循環的形式,其實就是一層的循環,所有時間複雜度爲O(N)

15、一棵非空的二叉樹的先序遍歷序列與後序遍歷序列正好相反,則該二叉樹一定滿足?

只有一個葉子結點

16、HTTPS是使用(   )來保證信息安全的.

SSL

17、1.不建議在構造函數中拋出異常;

2.構造函數拋出異常時,析構函數將不會被執行,需要手動的去釋放內存

1.析構函數不應該拋出異常;

2.當析構函數中會有一些可能發生異常時,那麼就必須要把這種可能發生的異常完全封裝在析構函數內部,決不能讓它拋出函數之外;

3. 析構函數異常相對要複雜一些,存在一種衝突狀態,程序將直接崩潰:異常的被稱爲“棧展開(stack unwinding)”【備註】的過程中時,從析構函數拋出異常,C++運行時系統會處於無法決斷的境遇,因此C++語言擔保,當處於這一點時,會調用 terminate()來殺死進程。因此,當處理另一個異常的過程中時,不要從析構函數拋出異常, 拋出異常時,其子對象將被逆序析構

18、

對於滿足SQL92標準的SQL語句:
select foo,count(foo)from pokes where foo>10group by foo having count (*)>5 order by foo 
其執行順序應該是?

FROM->WHERE->GROUP BY->HAVING->SELECT->ORDER BY


19、在網絡7層協議中,如果想使用UDP協議達到TCP協議的效果,可以在哪層做文章?

會話層


20、struct以最長的基本類型對齊。


21、開發C代碼時,經常見到如下類型的結構體定義:

typedef struct list_t{
struct list_t *next;
struct list_t *prev;
char data[0];
}list_t;

最後一行char data[0];的作用是?


A方便管理內存緩衝區

B減少內存碎片化

開發C代碼時,經常見到如下類型的結構體定義:

typedef struct list_t{
struct list_t *next;
struct list_t *prev;
char data[0];
}list_t;

最後一行char data[0];的作用是?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章