C/C++筆試題1(基礎題)

爲了便於溫故而知新,特於此整理 C/C++ 方面相關面試題。分享,共勉。html

(備註:各題的重要程度與前後順序無關。不斷更新中......歡迎補充)ios

(1)分析下面程序的輸出(* 與 -- 運算符優先級問題)c++

程序1:原題程序程序員

 1 #include<iostream>
 2 using namespace std;
 3 
 4 void main()
 5 {
 6     static int a[5] = {1, 2, 3, 4, 5}, b = 6;
 7     int *p = (int *)(&a + 1);
 8     cout << "*(a + 1) :: " << *(a + 1) << endl;
 9     cout << "*p :: " << *p << endl;
10     cout << "*p-- :: " << *p-- << endl;
11     cout << "*p---b :: " << *p---b << endl;
12     cout << "*p :: " << *p << endl;
13     cout << "*p-- :: " << *p-- << endl;
14 
15     system("pause");
16 }
17 // run out
18 /*
19 *(a + 1) :: 2
20 *p :: 6
21 *p-- :: 6
22 *p---b :: -1
23 *p :: 4
24 *p-- :: 4
25 請按任意鍵繼續. . .
26 */

總結:參考《C++操做符的優先級面試

程序2:對比分析數組

 1 #include<iostream>
 2 using namespace std;
 3 
 4 void main()
 5 {
 6     static int ar[5] = {1, 2, 3, 4, 5};
 7     int cr = 6;
 8     int *pr = (int *)(&ar + 1);
 9     cout << "*(ar + 1) :: " << *(ar + 1) << endl;
10     cout << "*pr :: " << *pr << endl;
11     cout << "*pr-- :: " << *pr-- << endl;
12     cout << "*pr :: " << *pr << endl;
13     cout << "*pr---cr :: " << *pr---cr << endl;
14     cout << "*pr :: " << *pr << endl;
15     cout << "*pr-- :: " << *pr-- << endl;
16 
17     system("pause");
18 }
19 
20 // run out
21 /*
22 *(ar + 1) :: 2
23 *pr :: 0
24 *pr-- :: 0
25 *pr :: 5
26 *pr---cr :: -1
27 *pr :: 4
28 *pr-- :: 4
29 請按任意鍵繼續. . .
30 */

(2)指針函數與函數指針的區別安全

指針函數是指一個返回指針類型的函數。函數指針是指一個指向函數的指針。數據結構

(3)函數模板與模板函數的區別模塊化

函數模板指由關鍵字template、typename(或class)定義的通用函數體。函數

將類型形參實例化的參數成爲模板實參。模板函數指用模板實參實例化的函數。

即模板函數指將函數模板的類型形參實例化的過程。

(4)static 在 C/C++ 語言中的做用

一、static在C語言中的做用

第1、修飾局部變量。static修飾的局部變量只執行一次初始化,且延長了局部變量的生命週期,直到程序運行結束之後才釋放。

  static修飾的局部變量存放在全局數據區的靜態變量區。初始化的時默認值爲0;

第2、修飾全局變量。static修飾的全局變量只能在本文件中訪問,不能在其它文件中訪問,即使是加extern外部聲明也不能夠。

第3、修飾函數。若static修飾一個函數,則這個函數的只能在本文件中調用,不能在其餘文件被調用。即具備文件做用域。

二、static在C++語言中的做用

第1、類中靜態成員分爲 靜態成員變量 和 靜態成員函數。

第2、靜態成員變量的名字在類的做用域中,能夠避免命名衝突。

第3、靜態成員變量獨立於該類的任何對象而存在。

第4、靜態成員變量能夠聲明爲任意類型:常量、引用、數組、類自己類型等。

第5、靜態成員變量必須在類的定義體外部初始化值。

第6、靜態成員函數與通常普通成員函數最大的區別在於不存在this指針。由於這個函數是與類相關的函數,而不是與某一個對象相關。

第7、聲明靜態成員函數時在前面加關鍵字static,當在類外實現這個函數時,不容許加關鍵字。

第8、能夠經過做用域操做符直接調用static靜態成員函數。或經過類的對象、引用或指向類對象的指針間接的調用static靜態成員函數。

第9、static靜態成員函數不是任何對象的組成部分,因此static成員函數不能被聲明爲const。

  (函數聲明爲const是對函數this指針的進一步限定,而static成員函數自己就不存在this指針,因此再加const是沒有意義。)

第10、static靜態成員函數不能夠被聲明爲虛函數。虛函數是爲實現多態的一種特殊成員函數,由於static函數沒有this指針,所以是沒有意義的。

三、參加隨筆《static關鍵字(1)

四、參加隨筆《static關鍵字(C/C++中的做用)

(5)相比於C函數,C++函數有哪些特點?

C++函數增長重載、內聯、const、virtual四種新機制。

重載和內聯機制既能夠用於全局函數也能夠用於類的成員函數。

const和virtual機制僅用於類的成員函數。

(6)重載、覆蓋、隱藏的區別

重載是指在同一個類中,同名函數,參數不一樣。重載的核心在於參數,參數有三個基本點:一、類型  二、數量  三、順序。(切記與返回值無關)。

注意:第1、做用域(全局函數與類中成員函數同名不算重載)。第2、類中成員函數加const修飾也算做重載範疇。

覆蓋是指派生類重寫(遵循三同原則)基類的虛函數。

隱藏分兩種狀況:

一則:派生類與基類的函數同名,但參數不一樣,與關鍵字virtual無關;(不一樣於重載)

二則:派生類與基類的函數同名,而且同參,但無關鍵字virtual;(不一樣於覆蓋)

(7)函數strlen與操做符sizeof的區別:參見隨筆《strlen與sizeof

(8)函數聲明。聲明函數原型時,函數每一個形參的名稱可省略,主要聲明清楚每一個參數的類型和返回值類型就能夠了。

(9)表達式。全部的表達式都有值。

(10)編譯。程序的編譯是以文件爲單位的,所以將程序分到多個文件中能夠減小每次對程序修改後再編譯所帶來的工做量。

(11)靜態成員變量。類的靜態數據成員變量須要在類定義外進行初始化。

(12)友元。當將一個類S定義爲另外一個類A的友元類時,類S的全部成員函數均可以直接訪問類A的全部成員(成員變量和成員函數)。

(13)C/C++內存分配區別:

一、malloc / free是C語言標準的庫函數;new / delete是C++中的運算符。 

二、都是在堆(heap)上進行動態的內存操做。

三、用malloc函數須要指定內存分配的字節數而且不能初始化對象;new 會自動調用對象的構造函數。

四、delete會調用對象的destructor,而free不會調用對象的destructor。

(14)若是建立的是靜態局部或全局對象,則對象的位模式所有爲0,不然默認值將會是隨機的。

(15)析構函數是特殊的類成員函數,它沒有返回類型、沒有參數、不能隨意調用、也沒有重載,只有在類對象的生命期結束時,由系統自動調用。

(16)類中成員對象的構造是按照在類中定義的順序進行的,而不是按照構造函數冒號後的初始化列表順序進行構造的(這點尤爲須要注意)。

(17)explicit關鍵字。普通構造函數能夠被隱式調用,而被關鍵字explicit修飾的構造函數只能被顯式調用。

(18)拷貝構造函數使用狀況:

一、一個對象以值傳遞的方式傳入函數體。

二、一個對象以值傳遞的方式從函數返回。

三、一個對象須要經過另一個對象進行初始化。

(19)在ANSI C 語言中用什麼來定義常量呢?答案是enum類型和#define宏,這兩個均可以用來定義常量。而在C++的類中實現常量,須要考慮使用枚舉。

(20)一個類的構造和析構函數都不能用const修飾。

(21)const修飾成員函數。const在類中對成員函數的三種做用(一、參數;二、返回值;三、函數體)。

(22)枚舉詳解

一、枚舉是一種類型。

二、默認的,第一個枚舉成員賦值爲0,後面的每一個枚舉成員的值比前面的大1。

三、枚舉成員自己是一個常量表達式,不能夠改變其值。

四、能夠顯式的定義枚舉成員的值,當隨機指定其中某個成員值後,位於其前的成員值仍爲默認值,位於其後的成員值比前面的大一。

(23)引用與指針的區別

一、初始化要求不一樣。前者必需要初始化。

二、可修改性不一樣。前者一旦被初始化,它就不能被另外一個對象引用,而指針在任什麼時候候均可以指向另外一個對象。

三、不存在NULL引用。引用必需要肯定具體引用的是某個對象。

四、測試的區別。引用不會指向空值,使用引用前不須要測試它的合法性。指針可能爲空值,使用前須要進行測試,避免空指針致使程序崩潰。

五、應用的區別。若是指向一個對象後就不會再改變指向,那麼選擇使用引用。若是在不一樣的時刻須要指向不一樣的對象,應該使用指針。

(24)內聯是以代碼膨脹爲代價的,僅僅省去了函數調用的開銷,從而提升了函數的執行效率,通常適合於使用頻率高,而且代碼量簡單的函數。

(25)內聯與宏的區別

內聯函數在編譯時展開,宏在預編譯時展開。

在編譯時內聯函數能夠直接被嵌入到目標代碼中,而宏在預處理是隻是僅僅的文本替換。

內聯函數能夠完成類型匹配,語句正確的判斷,而宏不具備。

宏不屬於函數,內聯函數屬於函數。

宏在定義時要當心處理宏參數,以避免出現二義性。

(26)頭文件中的ifndef/define/endif幹什麼用?防止頭文件被重複引用。

(27)#include <filename.h> 和 #include 「filename.h」 有什麼區別?

對於#include <filename.h> ,編譯器從標準庫路徑開始搜索 filename.h

對於#include 「filename.h」,編譯器從用戶的工做路徑開始搜索 filename.h

(28)const 有什麼用途?

一、const即「只讀」。能夠定義 const 常量。

二、const能夠修飾成員函數的參數、返回值,甚至函數的定義體。

被const 修飾的東西都受到強制保護,能夠預防意外的變更,能提升程序的健壯性。

三、參見隨筆《const

(29)在C++ 程序中調用被 C 編譯器編譯後的函數,爲何要加 extern 「C」?

C++語言支持函數重載,C 語言不支持函數重載。函數被C++編譯後在庫中的名字與C 語言的不一樣。假設某個函數的原型爲:

void foo(int x, int y);該函數被C 編譯器編譯後在庫中的名字爲_foo ,而C++編譯器則會產生像_foo_int_int 之類的名字。

C++提供了C 鏈接交換指定符號extern「C」來解決名字匹配問題。

(30)內存思考題1,代碼以下:

 1 void GetMemory(char *p)
 2 {
 3    p = (char *)malloc(100);
 4 }
 5 
 6 void Test()
 7 {
 8     char *str = NULL;
 9     GetMemory(str);
10     strcpy(str, "hello world");
11     printf(str);
12 }

請問運行Test 函數會有什麼樣的結果?

程序崩潰。由於GetMemory 並不能傳遞動態內存,

Test 函數中的 str 一直都是 NULL。

執行strcpy(str, "hello world"); 將使程序崩潰。

(31)內存思考題2,代碼以下:

 1 char *GetMemory(void)
 2 {
 3     char p[] = "hello world";
 4     return p;
 5 }
 6 
 7 void Test(void)
 8 {
 9     char *str = NULL;
10     str = GetMemory();
11     printf(str);
12 }

請問運行Test 函數會有什麼樣的結果?

多是亂碼。

由於GetMemory 返回的是指向「棧內存」的指針,該指針的地址不是 NULL,

但其原來的內容已經被清除,新內容不可知。

(32)內存思考題3,代碼以下:

 1 void GetMemory(char **p, int num)
 2 {
 3     *p = (char *)malloc(num);
 4 }
 5 
 6 void Test(void)
 7 {
 8     char *str = NULL;
 9     GetMemory(&str, 100);
10     strcpy(str, "hello");
11     printf(str);
12 }

請問運行Test 函數會有什麼樣的結果?

(1)可以輸出hello

(2)內存泄漏

(33)內存思考題4,代碼以下:

 1 void Test(void)
 2 {
 3     char *str = (char *) malloc(100);
 4     strcpy(str, "hello");
 5     free(str);
 6     if(str != NULL)
 7     {
 8         strcpy(str, "world");
 9         printf(str);
10     }
11 }

請問運行Test 函數會有什麼樣的結果?

篡改動態內存區的內容,後果難以預料,很是危險。

由於free(str);以後,str 成爲野指針,if (str != NULL)判斷語句不起做用。

(34)全局變量和局部變量有什麼區別?怎麼實現的?操做系統和編譯器是怎麼知道的?

全局變量的生命週期是整個程序運行期間,而局部變量的生命週期則是局部函數或過程調用的時間段。

其實現是由編譯器在編譯時採用不一樣內存分配方法。

全局變量在main函數調用前,就開始分配,若是是靜態變量則是在main函數前就已經初始化了。而局部變量則是在用戶棧中動態分配的。

(35)C語言文件讀寫程序

 1 #include "stdio.h"
 2 #include "stdlib.h"
 3 
 4 void main()
 5 {
 6     FILE *fp = NULL;
 7     char filename[10] = "test.txt";
 8     scanf("%s", filename);
 9     if ((fp = fopen(filename, "w")) == NULL)
10     {
11         printf("cann't open file\n");
12         exit(0);
13       }
14       char ch = getchar();
15       while (ch != '#')
16       {
17            fputc(ch, fp);
18            putchar(ch);
19            ch = getchar();
20       }
21 
22       fclose(fp);
23 }

(36)數組越界問題

下面這個程序執行後會有什麼錯誤或者效果:

1 #define MAX 255
2 
3 void main()
4 {
5     unsigned char A[MAX], i;
6     for (i = 0; i <= MAX; i++)
7         A[i] = i;
8 }

解答:

MAX = 255,數組A的下標範圍爲: 0..MAX-1,這是其一。

其二,當i循環到255時,循環內執行: A[255] = 255;

這句自己沒有問題,可是返回for (i = 0; i <= MAX; i++) 語句時,

因爲unsigned char的取值範圍在(0..255),i++之後i又爲0了,無限循環下去。

備註:char類型爲一個字節,取值範圍是[-128,127],unsigned char [0 ,255]

(37)C 和 C++ 中的 struct 有什麼不一樣?

 C 和 C++中struct的主要區別是 C 中的struct不能夠含有成員函數,而 C++ 中的struct能夠。

(38)C++中的struct 與 class 有什麼不一樣?

C++中的struct 和 class的區別是默認成員訪問權限不一樣。struct的默認訪問權限是public,而class的默認訪問權限是private。

(39)Heap與Stack的區別:

Heap是堆,Stack是棧。

Stack的空間由操做系統自動分配/釋放,Heap上的空間手動分配/釋放。

Stack空間有限,Heap是很大的自由存儲區

C中的malloc函數分配的內存空間即在堆上。C++中對應的是new操做符。

程序在編譯期對變量和函數分配內存都在棧上進行,且程序運行過程當中函數調用時參數的傳遞也在棧上進行。

(40)請定義一個宏,比較兩個數的a、b的大小,不能使用大於、小於、if語句。

 1 #include<iostream>
 2 using namespace std;
 3 
 4 #define max0(a, b)  (((a)-(b)) & (1<<31)) == 1 ? (a) : (b);
 5 #define max1(a, b)  ((((a)-(b)) & (1<<31)) ? (b) : (a))
 6 #define max2(a, b)  ((((long)((a)-(b))) & 0x80000000) ? (b) : (a))
 7 #define max3(a, b)  (((abs((a)-(b))) == ((a)-(b))) ? (a) : (b))
 8 
 9 void main()
10 {
11     int n0 = max0(5, 6);
12     cout << n0 << endl;
13 
14     int n1 = max1(10, 26);
15     cout << n1 << endl;
16     
17     int n2 = max2(5, 1);
18     cout << n2 << endl;
19 
20     int n3 = max3(100, 47);
21     cout << n3 << endl;
22 
23     system("pause");
24 }
25 // run out
26 /*
27 6
28 26
29 5
30 100
31 請按任意鍵繼續. . .
32 */

(41)交換兩個數的方法。參見隨筆《交換兩個數的方法

(42)設置或者清除某位。參見隨筆《面試題(1)

(43)求最大字段和。參見隨筆《面試題(1)

(44)字節對齊。參見隨筆《面試題(1)

(45)大小端判斷。參見隨筆《面試題(1)

(46)查找數組中的最小和次小項。參見隨筆《面試題(1)

(47)用預處理指令#define 聲明一個常數,用以代表1年中有多少秒(忽略閏年問題)。

1 #define  SECONDS_PER_YEAR  (60 * 60 * 24 * 365)UL

分析如下幾點:

一、 #define 語法的基本知識(例如:不能以分號結束,括號的使用等等)。

二、 懂得預處理器將爲你計算常數表達式的值,所以,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。

三、 意識到這個表達式將使一個16位機的整型數溢出-所以要用到長整型符號L,告訴編譯器這個常數是的長整型數。

四、 若是你在你的表達式中用到UL(表示無符號長整型),那麼你有了一個好的起點。記住,第一印象很重要。

(48)用變量a給出下面的定義

a) 一個整型數(An integer)

b) 一個指向整型數的指針(A pointer to an integer)

c) 一個指向指針的的指針,它指向的指針是指向一個整型數(A pointer to a pointer to an integer)

d) 一個有10個整型數的數組(An array of 10 integers)

e) 一個有10個指針的數組,該指針是指向一個整型數的(An array of 10 pointers to integers)

f) 一個指向有10個整型數數組的指針(A pointer to an array of 10 integers)

g) 一個指向函數的指針,該函數有一個整型參數並返回一個整型數

(A pointer to a function that takes an integer as an argument and returns an integer)

h) 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數

( An array of ten pointers to functions that take an integer argument and return an integer )

 1 a) int a; // An integer
 2 b) int *a; // A pointer to an integer
 3 c) int **a; // A pointer to a pointer to an integer
 4 d) int a[10]; // An array of 10 integers
 5 e) int *a[10]; // An array of 10 pointers to integers
 6 f) int (*a)[10]; // A pointer to an array of 10 integers
 7 g) int (*a)(int); 
 8 // A pointer to a function a that takes an integer argument and returns an integer
 9 h) int (*a[10])(int); 
10 // An array of 10 pointers to functions that take an integer argument and return an integer

(49)關鍵字typedef 與 宏的區別。參見隨筆《typedef關鍵字

(50)如何引用一個已經定義過的全局變量?

能夠用引用頭文件的方式,或者能夠用extern關鍵字。

若是用引用頭文件方式來引用某個在頭文件中聲明的全局變量,假定你將那個變量寫錯了,那麼在編譯期間會報錯。

若是你用extern方式引用時,假定你犯了一樣的錯誤,那麼在編譯期間不會報錯,而在連接期間報錯。

(51)全局變量和局部變量在內存中有什麼區別?

全局變量儲存在靜態數據區,局部變量在堆棧中。

(52)零值比較

(53)sizeof的使用

(54)堆棧溢出通常是由什麼緣由致使的?

沒有回收垃圾資源。

(55)什麼函數不能聲明爲虛函數?

constructor構造函數

(56)不能作switch()的參數類型是:

switch的參數不能爲實型。

(57)局部變量可否和全局變量重名?

能,局部會屏蔽全局。要用全局變量,須要使用"::"

局部變量能夠與全局變量同名,在函數內引用這個變量時,會用到同名的局部變量,而不會用到全局變量。

對於有些編譯器而言,在同一個函數內能夠定義多個同名的局部變量,好比在兩個循環體內都定義一個同名的局部變量,而那個局部變量的做用域就在那個循環體內。

(58)全局變量可不能夠定義在可被多個.C文件包含的頭文件中?爲何?

能夠。在不一樣的C文件中以static形式來聲明同名全局變量。

能夠在不一樣的C文件中聲明同名的全局變量,前提是其中只能有一個C文件中對此變量賦初值,此時連接不會出錯。

(59)do……while和while……do有什麼區別?

前一個循環一遍再判斷,後一個判斷之後再循環。

(60)語句for( ;1 ;)有什麼問題?它是什麼意思?

無限循環,和while(1)相同。

(61)寫出下面程序的輸出內容

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 void main()
 4 {
 5     int a,b,c,d;
 6     a = 10;
 7     b = a++;
 8     c = ++a;
 9     d = 10 * a++;
10     printf("b,c,d:%d,%d,%d\n", b, c, d);
11     system("pause");
12 }
13 //run out:
14 /*
15 b,c,d:10,12,120
16 請按任意鍵繼續. . .
17 */

(62)寫出下列代碼的輸出內容

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 typedef union 
 5 {
 6     long i; 
 7     int k[5]; 
 8     char c;
 9 } DATE;
10 
11 struct data 
12 { 
13     int cat; 
14     DATE cow; 
15     double dog;
16 }too;
17 
18 void main()
19 {
20     DATE max;
21     printf("%d, %d\n", sizeof(long), (sizeof(struct data) + sizeof(max)));
22     system("pause");
23 }
24 // run out:
25 /*
26 4, 52
27 請按任意鍵繼續. . .
28 */

分析:DATE是一個union, 變量共用空間,裏面最大的變量類型是int[5], 佔用20個字節,因此它的大小是20。

data是一個struct, 每一個變量分開佔用空間. 依次爲int(4)+ DATE(20)+ double(8) = 32

因此結果是 20 + 32 = 52

(63)寫出下列代碼的輸出內容

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 int inc(int a)
 5 {
 6     return (++a);
 7 }
 8 int multi(int* a, int* b, int* c)
 9 {
10     return (*c = *a * *b);
11 }
12 
13 typedef int(*FUNC1) (int in);
14 typedef int(*FUNC2) (int*, int*, int*);
15 
16 void show(FUNC2 fun, int arg1, int*arg2)
17 {
18     FUNC1 p = &inc;
19     int temp = p(arg1);
20     fun(&temp, &arg1, arg2);
21     printf("%d\n", *arg2);
22 }
23 
24 void main()
25 {
26     int a;
27     show(multi, 10, &a);
28     system("pause");
29 }
30 // run out:
31 /*
32 110
33 請按任意鍵繼續. . .
34 */

(64)找出下面代碼中的因此錯誤(下面爲正常標準程序)

說明:如下代碼是把一個字符串倒序,如「abcd」倒序後變爲「dcba」

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 void main()
 6 {
 7     char* src = "hello,world";
 8     int len = strlen(src);
 9     char* dest = (char*)malloc(len + 1); // 要爲\0分配一個空間
10     char* d = dest;
11     char* s = &src[len-1]; // 指向最後一個字符
12     while ( len-- != 0 )
13         *d++ = *s--;
14     *d = 0; // 尾部要加\0
15     printf("%s\n", dest);
16     free(dest);// 使用完,應當釋放空間,以避免形成內存泄露
17 
18     system("pause");
19 }

(65)對於一個頻繁使用的短小函數,在C語言中應用什麼實現,在C++中應用什麼實現?

C語言用宏定義,C++用inline實現。

(66)下面的程序是否有錯誤,若是有錯,請說明緣由。

1 char* const pszHelp = "hello";
2 pszHelp[0] = 'a';

由於pszHelp指向一個常量字符串,因此根本不容許修改字符串內容。除非使用一個字符數組。

(67)什麼是抽象類

包含抽象函數的類是抽象類,知足virtual fun() = 0; 的語法的函數是抽象函數。主要用於提供接口。

(68)何時須要使用虛析構函數

通常狀況下,類的析構函數都定義成虛函數,主要是考慮在使用基類指針操做派生類對象時保證類的析構順序。

(69)請指出下面代碼存在的潛在問題

 1 class CC 
 2 {
 3     int* m_pCount;
 4 
 5 public:
 6     void clear() 
 7     { 
 8         if ( m_pCount ) 
 9             delete m_pCount; 
10     }
11 
12     CC() 
13     { 
14         m_pCount = new int; 
15     }
16 
17     ~CC() 
18     { 
19         clear(); 
20     }
21 };

主要存在的問題是clear函數在delete m_pCount; 後並無置指針爲空( m_pCount = NULL),這樣當第二次調用 clear 時,會出現問題。

(70)請寫出下列程序運行的結果

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class A 
 5 {
 6     public:
 7         virtual void func() { cout << "I am in base" << endl; };
 8 };
 9 
10 class B : public A 
11 {
12     public:
13         virtual void func() { cout << "I am in derived" << endl; }
14 };
15 
16 void main() 
17 {
18     B* bb = new B;
19     bb->func();
20     A* aa = (A*)bb;
21     aa->func();
22     system("pause");
23 }
24 // run out
25 /*
26 I am in derived
27 I am in derived
28 請按任意鍵繼續. . .
29 */

(71)Debug版本中常用ASSERT進行斷言,在Release版本中有一個起一樣做用的函數,請說明。

VERIFY,並且要注意ASSERT中的語句在Release版本中會忽略。

(72)描述內存分配方式以及它們的區別?

1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static 變量。

2) 在棧上建立。在執行函數時,函數內局部變量的存儲單元均可以在棧上建立,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集。

3) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc 或new 申請任意多少的內存,程序員本身負責在什麼時候用free 或delete 釋放內存。

動態內存的生存期由程序員決定,使用很是靈活。

(73)C++四種類型轉換方式

一、static_cast  二、const_cast  三、dynamic_cast  四、reinterpret_cast.

關於各類方式區別參見隨筆《C++類型轉換

(74)類成員函數的重載、覆蓋和隱藏區別?

a、成員函數被重載的特徵:

  一、相同的範圍(在同一個類中);

  二、函數名字相同;

  三、參數不一樣;

  四、virtual 關鍵字無關緊要。

b、覆蓋是指派生類函數覆蓋基類函數,特徵是:

  一、不一樣的範圍(分別位於派生類與基類);

  二、函數名字相同;

  三、參數相同;

  四、基類函數必須有virtual 關鍵字。

c、「隱藏」是指派生類的函數屏蔽了與其同名的基類函數,規則以下:

  一、若是派生類的函數與基類的函數同名,可是參數不一樣。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。

  二、若是派生類的函數與基類的函數同名,而且參數也相同,可是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。

(75)如何打印出當前源文件的文件名以及源文件的當前行號?

cout << __FILE__ ;

cout << __LINE__ ;

__FILE__和__LINE__是系統預約義宏,這種宏並非在某個文件中定義的,而是由編譯器定義的。

(76)main 函數執行之前,還會執行什麼代碼?

全局對象的構造函數會在main 函數以前執行。

(77)main 主函數執行完畢後,是否可能會再執行一段代碼,給出說明?

能夠,能夠用_onexit 註冊一個函數,它會在main 以後執行。示例代碼以下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 int fn1() 
 5 { 
 6     printf( "next. " ); 
 7     system("pause"); 
 8     return 0;
 9 }
10 
11 int fn2() 
12 { 
13     printf( "executed " ); 
14     return 0; 
15 } 
16 
17 int fn3() 
18 { 
19     printf( "is " ); 
20     return 0; 
21 } 
22 
23 int fn4() 
24 { 
25     printf( "This " ); 
26     return 0; 
27 } 
28 
29 void main() 
30 {
31     _onexit( fn1 ); 
32     _onexit( fn2 ); 
33     _onexit( fn3 ); 
34     _onexit( fn4 ); 
35     printf( "This is executed first. " );
36     system("pause");
37 } 
38 // run out
39 /*
40 This is executed first. 請按任意鍵繼續. . .
41 This is executed next. 請按任意鍵繼續. . .
42 */

(78)如何判斷一段程序是由C 編譯程序仍是由C++編譯程序編譯的?

1     #ifdef __cplusplus 
2         cout << "c++"; 
3     #else 
4         cout << "c"; 
5     #endif 

(79)寫出下列程序的輸出內容

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 void  main()
 4 {
 5     int x = 3, y, z;
 6     x *= (y = z = 4);
 7     printf("x = %d\n", x);
 8     z = 2;
 9     x = (y = z);
10     printf("x = %d\n", x);
11     x = (y == z);
12     printf("x = %d\n", x);
13     system("pause");
14 }
15 
16 //Out  put :
17 /*
18 x = 12
19 x = 2
20 x = 1
21 請按任意鍵繼續. . .
22 */

(80)寫出下列程序的輸出內容

 1 #include<iostream>
 2 using namespace std;
 3 
 4 class Test
 5 {
 6 private:
 7     int value;
 8 
 9 public:
10     Test(int x = 0) : value(x)
11     {}
12     ~Test()
13     {}
14     operator int()
15     {
16         return value;
17     }
18     operator bool()
19     {
20         return value > 0;
21     }
22 };
23 
24 void main()
25 {
26     Test t1(1);
27     
28     if (t1)
29     {
30         cout << "if(t1)" << endl;
31     }
32     
33     switch (t1.operator int())
34     {
35         case 1:
36             cout << "switch(t1)" << endl;
37             break;
38     };
39 
40     system("pause");
41 }
42 
43 //run out:
44 /*
45 if(t1)
46 switch(t1)
47 請按任意鍵繼續. . .
48 */

(81)寫出下列程序的輸出內容

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 void  main()
 6 {
 7     char ch[] = {"abcd"};
 8     char *r = ch + 1;
 9     ch[0] = (*r++) + 2;
10     cout << "第一部分結果:" << endl;
11     cout << sizeof(ch) << endl;
12     cout << sizeof(r) <<endl;
13     cout << sizeof(*r) <<endl;
14     cout << ch << endl;
15     cout << *r << endl;
16     cout << ch[0] << endl;
17     cout << endl;
18 
19 
20     char *ptr = "abcdef";
21     cout << ptr[3] << endl << endl;   
22 
23     cout << "第二部分結果:" << endl;
24     //                 A       B       C           D
25     char *str[] = {"Welcome","To","Fortemedia","Nanjing"};
26     char **p = str + 1;   
27     //執行結束    p指向B
28     str[0] = (*p++) + 2;
29     //執行結束    p指向C  可是str[0]指向D後面的元素,所以內容爲空
30     str[1] = *(p+1);
31     //執行結束    p指向C不變   str[1]指向p後一個元素的地址,即就是D
32     str[2] = p[1] + 3;
33     //p是二級指針,此時p[1]指向D   p[1]+3即就是字符串元素的第四個字符,即‘j’ str[2]='jing'
34     str[3] = p[0] + (str[2] - str[1]);
35     //str[2] - str[1] = 3, 而p[0]指向的是‘j’的地址,因此str[4] = 'g'
36     cout << sizeof(str) << endl; // 16
37     cout << p << " :: " << sizeof(p) << endl;   // 4
38     cout << *p << " :: " << sizeof(*p) << endl; // jing::4
39     cout << **p << " :: " << sizeof(**p) << endl; // j::1
40     cout << str[0] << endl;
41     cout << str[1] << endl; // Nanjing
42     cout << str[2] << endl; // jing
43     cout << str[3] << endl; // g
44     
45     cout << str << endl;
46     system("pause");
47 }
48 //run out:
49 /*
50 第一部分結果:
51 5
52 4
53 1
54 dbcd
55 c
56 d
57 
58 d
59 
60 第二部分結果:
61 16
62 0018F83C :: 4
63 jing :: 4
64 j :: 1
65 
66 Nanjing
67 jing
68 g
69 0018F834
70 請按任意鍵繼續. . .
71 */

(82)C++中虛函數和純虛函數的區別? 

一、聲明的差別。純虛函數的聲明除過像虛函數加關鍵字virtual而外,還必須加 = 0;

聲明爲虛函數的做用是爲了能讓這個函數在它的派生類裏面被覆蓋或隱藏,這樣編譯器就可使用後期綁定來達到多態性。

聲明純虛函數,有一種接口的規範做用。派生類必須且只能原樣實現。

二、定義的差別。虛函數在基類中必須實現(哪怕空操做),派生類裏面也能夠不覆蓋或隱藏。但純虛函數必須在派生類裏面去實現。

三、虛基類或抽象類。帶純虛函數的類叫作虛基類,或抽象類。這種基類不能直接聲明對象,只能被繼承,而且只有在實現其純虛函數後才能使用。

(83)何時須要「引用」?

流操做符<< 和 >>、賦值操做符=的返回值、拷貝構造函數的參數、賦值操做符=的參數、其它狀況都推薦使用引用。

(84)結構與聯合的區別?

1. 結構和聯合都是由多個不一樣的數據類型成員組成。

但在任何同一時刻, 聯合中只存放了一個被選中的成員(全部成員共用一塊地址空間), 而結構的全部成員都存在(不一樣成員的存放地址不一樣)。

2. 對於聯合的不一樣成員賦值, 將會對其它成員重寫,  原來成員的值就不存在了, 而對於結構的不一樣成員賦值是互不影響的。

(85)下列關於聯合的程序輸出

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 union
 5 {
 6     int i;
 7     char x[2];
 8 } a;
 9 
10 void main()
11 {
12     a.x[0] = 10;
13     a.x[1] = 1;
14     printf("%d", a.i);  // 266
15     system("pause");
16 }

低位低地址,高位高地址,內存佔用狀況是Ox010A

(86)New delete 與 malloc free 的聯繫與區別

都是在堆(heap)上進行動態的內存操做。用malloc函數須要指定內存分配的字節數而且不能初始化對象,new 會自動調用對象的構造函數。

delete 會調用對象的destructor,而free不會調用對象的destructor。

關於new 和 delete詳情內容請參見隨筆《new 與 delete

(87)C++是否是類型安全的?

不是。兩個不一樣類型的指針之間能夠強制轉換(用reinterpret cast)。C#是類型安全的。

(88)當一個類A中沒有生命任何成員變量與成員函數,這時sizeof(A)的值是多少,若是不是零,請解釋一下編譯器爲何沒有讓它爲零。

確定不是零。舉個反例,若是是零的話,聲明一個class A[10]對象數組,而每個對象佔用的空間是零,這時就沒辦法區分A[0],A[1]…了。

(89)簡述數組與指針的區別?

數組要麼在靜態存儲區被建立(如全局數組),要麼在棧上被建立。指針能夠隨時指向任意類型的內存塊。

(90)strcpy與memcpy的區別

一、複製的內容不一樣。strcpy僅僅複製字符串,而memcpy能夠複製任何類型的數據內容

二、複製的方法不一樣。strcpy不須要指定長度,它遇到字符串結束符"\0"便結束。memcpy則是根據其第三個參數決定複製的長度。

三、用途不一樣。一般在複製字符串時用strcpy,而複製其它類型數據時通常用memcpy()

(91)寫出下列程序的輸出內容:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 void  main()
 5 {
 6     unsigned short int i = 0;
 7     unsigned char ii = 255;
 8     int j = 8, p, q;
 9 
10     i = i - 1;
11     ii = ii + 1;
12     p = j << 1;
13     q = j >> 1;
14 
15     cout << i << endl;    // 65535
16     cout << ii << endl;
17     cout << p << endl;    // 16
18     cout << q << endl;    // 4
19 
20     system("pause");
21 }

(92)寫出下列程序的輸出內容:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 class Base
 5 {
 6 public:
 7     virtual void fun1()
 8     {
 9         cout << "Base :: fun1()" << endl;
10     }
11     virtual void fun2()
12     {
13         cout << "Base :: fun2()" << endl;
14     }
15     virtual void fun3()
16     {
17         cout << "Base :: fun3()" << endl;
18     }
19 
20 private:
21     int num1;
22     int num2;
23 };
24 
25 typedef void (*Fun)();
26 
27 void main()
28 {
29     Base b;
30     Fun pFun;
31     pFun = (Fun)*((int *)*(int *)(&b) + 0);
32     pFun();
33     pFun = (Fun)*((int *)*(int *)(&b) + 1);
34     pFun();
35     pFun = (Fun)*((int *)*(int *)(&b) + 2);
36     pFun();
37 
38     system("pause");
39 }
40 // run out:
41 /*
42 Base :: fun1()
43 Base :: fun2()
44 Base :: fun3()
45 請按任意鍵繼續. . .
46 */

分析:

typedef void( *Fun )( void );  // Fun定義爲一個沒有參數,返回void類型的函數指針

針對 *( ( int* ) * ( int* )( &b ) + i ); 這一段:

(int*)* 至關於沒有進行任何操做,因此等同於:

*( ( int* )( &b ) + i )

這裏先取b的地址,而後把地址轉換成int*,以後+i是指針算術,也就是在b的地址上加一個int的長度。

最後,前面的*是解指針,這段最後返回的是「b的地址 + i個int長度」的地址值。

最前面的(Fun)是強制把「b的地址 + i個int長度」的地址值轉換爲一個「沒有參數,返回void類型的函數指針」,

因此pFun就是一個函數指針,其指向的位置從一開始的b的地址,每次循環加一個int類型的長度。

而後,咱們來看實際狀況:

( Fun )*( ( int* ) * ( int* )( &b ) + i );

這裏*( ( int* ) * ( int* )( &b ) + i )最前面的*是其前面結果進行解指針,也就取b的地址+i個int長度的這個地址的值。

並把它轉換爲Fun類型,也就是一個沒有參數,返回void類型的函數指針,因此最後獲得的就是一個函數指針。

再來看循環,循環3次,pFun變量分別被賦了3次值,每次都是一個函數指針。

因爲Base類型中有virtual虛函數,因此b的地址指向的是b的vtbl(虛函數表),vtbl虛函數表能夠看做是一個保存了函數指針的數組,

每一個元素就是一個int長度,在vtbl虛函數表中Base::fun1, Base::fun2, Base::fun3是按照如上順序排列的,因此第一次循環指向Base::fun1。

那麼後兩次就指向Base::fun2 和 Base::fun3了。編碼以明志,調試而致遠。調試結果以下:

至於爲何是按照這樣的順序排列的,由於其聲明順序。

(93)寫出下列程序的輸出內容

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class A
 5 { 
 6     virtual void g() 
 7     { 
 8         cout << "A::g" << endl; 
 9     } 
10 private: 
11     virtual void f() 
12     { 
13         cout << "A::f" << endl; 
14     } 
15 }; 
16 
17 class B : public A 
18 { 
19     void g() 
20     { 
21         cout << "B::g" << endl; 
22     } 
23     virtual void h() 
24     { 
25         cout << "B::h" << endl; 
26     } 
27 };
28 
29 typedef void( *Fun )( void ); 
30 
31 int main() 
32 { 
33     B b; 
34     Fun pFun; 
35     for (int i = 0 ; i < 3; ++i) 
36     { 
37         pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i ); 
38         pFun(); 
39     }
40 
41     system("pause");
42 } 
43 */
44 // run out:
45 /*
46 B::g 
47 A::f 
48 B::h
49 */

(94)在何時須要使用「常引用」? 

若是既要利用引用提升程序的效率,又要保護傳遞給函數的數據不在函數中被改變,就應使用常引用。

常引用聲明方式:const 類型標識符 & 引用名 = 目標變量名;

(95)流操做符重載返回值申明爲「引用」的做用:

流操做符< <和>>,這兩個操做符經常但願被連續使用,例如:cout << "hello" << endl; 所以這兩個操做符的返回值應該是一個仍然支持這兩個操做符的流引用。

可選的其它方案包括:返回一個流對象和返回一個流對象指針。

可是對於返回一個流對象,程序必須從新(拷貝)構造一個新的流對象,也就是說,連續的兩個<<操做符其實是針對不一樣對象的!

這沒法讓人接受。對於返回一個流指針則不能連續使用<<操做符。所以,返回一個流對象引用是唯一選擇。

這個惟一選擇很關鍵,它說明了引用的重要性以及無可替代性,也許這就是C++語言中引入引用這個概念的緣由吧。賦值操做符=。

這個操做符象流操做符同樣,是能夠連續使用的,例如:x = j = 10; 或者 (x = 10) = 100;賦值操做符的返回值必須是一個左值,以即可以被繼續賦值。

所以引用成了這個操做符的唯一返回值選擇。

(96)系統爲變量賦的默認值打印結果:(系統:win32 + VS2010)

 1 #include <iostream>
 2 using namespace std;
 3 
 4 static int g_sn;
 5 int g_n;
 6 
 7 void func()
 8 { 
 9     static int inner_sn;
10     int inner_n;
11 
12     cout << "inner_sn :: " << inner_sn << endl;
13     // cout << inner_n << endl;  打印崩潰!系統不會自動爲局部變量賦初值。
14 }
15 
16 class A
17 {
18 private:
19     char m_ch;
20     int m_n;
21     bool m_b;
22     short m_sh;
23     long m_lg;
24     float m_fl;
25     double m_d;
26     int* m_pN;
27 
28 public:
29     void printDefaultValue()
30     {
31         cout << "char :: " << m_ch << endl;
32         cout << "int :: " <<  m_n << endl;
33         cout << "bool :: " <<  m_b << endl;
34         cout << "short :: " << m_sh << endl;
35         cout << "long :: " << m_lg << endl;
36         cout << "float :: " << m_fl << endl;
37         cout << "double :: " << m_d << endl;
38         cout << "int* :: " << m_pN << endl;
39     }
40 };
41 
42 void main()
43 {
44     cout << "g_sn :: " << g_sn << endl;
45     cout << "g_n :: " << g_n << endl;
46     func();
47     A objA;
48     objA.printDefaultValue();
49     system("pause");
50 }
51 
52 // run out:
53 /*
54 g_sn :: 0
55 g_n :: 0
56 inner_sn :: 0
57 char ::
58 int :: -858993460
59 bool :: 204
60 short :: -13108
61 long :: -858993460
62 float :: -1.07374e+008
63 double :: -9.25596e+061
64 int* :: CCCCCCCC
65 請按任意鍵繼續. . .
66 */

注意:系統不會爲局部變量賦默認值。所以直接打印局部變量的默認值致使崩潰!

(97)有哪幾種狀況只能用intialization list 而不能用assignment?

類中含有const、reference 成員變量;基類的構造函數都須要初始化表。

(98)面向對象的三個基本特徵,並簡單敘述之?

一、封裝:類。將客觀事物抽象成類,每一個類對自身的數據和方法實行授權限訪問protection (private, protected, public)。

二、繼承:廣義的繼承有三種實現形式:

實現繼承(指使用基類的屬性和方法而無需額外編碼的能力)、

可視繼承(子窗體使用父窗體的外觀和實現代碼)、

接口繼承(僅使用屬性和方法,實現滯後到子類實現)。前兩種(類繼承)和後一種(對象組合 => 接口繼承以及純虛函數)構成了功能複用的兩種方式。

三、多態:是將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值以後,父對象就能夠根據當前賦值給它的子對象的特性以不一樣的方式運做。

簡單的說,就是一句話:容許將子類類型的指針賦值給父類類型的指針。

(99)找出下列代碼的問題:

 1 void test() 
 2 { 
 3     char string[10], str1[10]; 
 4     int i; 
 5     for (i = 0; i < 10; i++) 
 6     { 
 7         str1[i] = 'a'; 
 8     } 
 9     strcpy(string, str1); 
10 } 

若是面試者指出字符數組str1不能在數組內結束能夠給3分;

若是面試者指出strcpy(string, str1)調用使得從str1內存起復制到string內存起所複製的字節數具備不肯定性能夠給7分,在此基礎上指出庫函數strcpy工做方式的給10 分;

str1不能在數組內結束:由於str1的存儲爲:{a, a, a, a, a, a, a, a, a, a},沒有'\0'(字符串結束符),因此不能結束。

strcpy( char *s1, char *s2)他的工做原理:

掃描s2指向的內存,逐個字符付到s1所指向的內存,直到碰到'\0',由於str1結尾沒有'\0'。因此具備不肯定性,不知道他後面還會賦值什麼東東。

正確程序以下:

 1 void test() 
 2 { 
 3     char string[10], str1[10]; 
 4     int i; 
 5     for (i = 0; i < 9; i++) 
 6     { 
 7         str1[i] = 'a' + i;  // 把abcdefghi賦值給字符數組 
 8     } 
 9     str1[i] = '\0'; // 加上結束符 
10     strcpy(string, str1); 
11 } 

(100)C++中爲何用模板類?

一、可用來建立動態增加和減少的數據結構。

二、它是類型無關的,所以具備很高的可複用性。

三、它在編譯時而不是運行時檢查數據類型,保證了類型安全。

四、它是平臺無關的,可移植性。

五、可用於基本數據類型。

(101)MFC中CString是類型安全類麼?

不是,其它數據類型轉換到CString可使用CString的成員函數Format來轉換

(102)函數模板與類模板有什麼區別?

函數模板的實例化是由編譯程序在處理函數調用時自動完成的,而類模板的實例化必須由程序員在程序中顯式地指定。

(103)動態鏈接庫的兩種方式?

調用一個DLL中的函數有兩種方法:

一、載入時動態連接(load-time dynamic linking),模塊很是明確調用某個導出函數,使得他們就像本地函數同樣。

這須要連接時連接那些函數所在DLL的導入庫,導入庫向系統提供了載入DLL時所需的信息及DLL函數定位。

二、運行時動態連接(run-time dynamic linking),運行時能夠經過LoadLibrary或LoadLibraryEx函數載入DLL。

DLL載入後,模塊能夠經過調用GetProcAddress獲取DLL函數的出口地址,而後就能夠經過返回的函數指針調用DLL函數了。

(104)類中函數詳解(構造函數、析構函數、拷貝構造函數、賦值構造函數)。請參見隨筆《類中函數

(105)寫出下列程序編譯錯誤問題的緣由

程序代碼以下:

 1 #define charPtr char *
 2 
 3 void main()
 4 {
 5     typedef char* charPtrDef;
 6     char str[4] = {"abc"};
 7     const char* p1 = str;
 8     const charPtr p2 = str;
 9     const charPtrDef p3 = str;
10     char * const p4 = str;
11     const char * const p5 = str;
12     p1++;
13     p2++;   // OK 宏定義僅僅只是文本替換,其本質與p1相同。能夠編譯經過
14 //  *p2 = 'A'; // error! const 修飾的是p2指向的內容,只讀不容許修改
15 //  p3++;   // error! const修飾的是p3指針,只讀不容許修改
16     *p3 = 'B'; // OK 所以指針指向的內容能夠修改
17 //  p4++;   // error! const修飾的是p4指針,只讀不容許修改
18     *p4 = 'C';  // OK 所以指針指向的內容能夠修改
19 //  p5++;   // error! *號後的const修飾的是p5指針,只讀不容許修改
20 //  *p5 = 'D'; // error! *號前的const修飾的是p5指針指向的內容,只讀不容許修改
21 }

註釋爲緣由分析。

(106)「引用」與多態的關係?

引用是除指針外另外一個能夠產生多態效果的手段。這意味着,一個基類的引用能夠指向它的派生類實例。

(107)多態的做用?

主要是兩個:

一、 隱藏實現細節,使得代碼可以模塊化;擴展代碼模塊,實現代碼重用;

二、 接口重用:爲了類在繼承和派生的時候,保證使用家族中任一類實例的某一屬性時的正確調用。

(108)STL中容器map,map有幾種賦值方式?每種方式有何區別?

詳情請參見隨筆《STL容器之map

(109)STL中容器list,使用list容器的merge方法須要注意什麼?

詳情請參見隨筆《STL容器之list

(110)介於vector和list之間的一種容器deque,請參見隨筆《STL容器之deque

(111)C++語言中如何訪問一個類私有成員變量?

兩種方式。第一,接口,利用訪問成員變量相應的set/get公共成員函數;第二,友元,友元類和友元函數。

(112)因爲本文篇幅太長,編輯後保存太慢。下面續《 C++筆試題2(基礎題)

 

Good Good Study, Day Day Up.

順序 選擇 循環 總結

相關文章
相關標籤/搜索