C/C++面试题分享
1、指針和引用的區別?
答:引用是在C++中引入的。它們之間的區別有:
(1)? ?? ? 非空區別:指針可以為空,而引用不能為空
(2)? ?? ? 可修改區別:如果指針不是常指針,那么就可以修改指向,而引用不能
(3)? ?? ? 初始化區別:指針在定義時可以不用初始化,而引用在定義的同時必須初始化
2、為什么構造函數不能聲明為虛函數?
答:因為虛函數采用的是虛調用的方法,虛調用是指允許在只知道部分信息的情況下的工作機制,特別允許我們調用一個只知道接口而不知道其對象的準確類型的函數。但是如果我們要調用構造函數創建對象時,必須要知道對象的準確類型,因此構造函數不能為虛函數。
3、char str1[]=”abc”; char str2[] = “abc”;? ?str1==str2為FALSE,因為str1和str2是位于堆棧上的,它們占用不同的內存空間。Const char str3[] = “abc”; const char str4[] = “abc”;str3==str4為FALSE,同樣它們是位于堆棧上的內存空間,是不同的。Const char *str5=”abc”, const char *str6=”abc”;char *str7=”abc”,char *str8 = “abc”,str5==str6 str7==str8為TRUE,因為”abc”是位于文字常量區的,系統會將幾個“abc”進行優化,使它們位于同一塊內存區,因此指針的指向也就相同了。
4、以下函數能求出數組的長度嗎?
void fun(char str[])
{
? ?? ? int len = sizeof(str)/sizeof(str[0]);
}
答:不能,數組作為參數傳遞給函數時,數組名被退化為指針,因此函數中的sizeof(str)實際是在求一個指針的sizeof,答案為4,因此不能計算出數組的長度。
5、一個32位的機器,該機器的指針是多少位?
答:指針是多少位只要看地址總線的位數就行了,80386以后的機子都是32的數據總線。所以指針的位數就是4個字節。即有void *p; 則sizeof(p) = 4。
6、C和C++中的struct和class有什么不同?
答:C和C++中struct的區別是C中的struct不能有成員函數,而C++中的struct可以。C++中struct和class的主要區別是默認的存取權限,struct的默認存取權限為public,而class的默認存取權限為private。
7、類的靜態成員和非靜態成員有何區別?
答:類的靜態成員每個類只有一個,靜態成員為所有類的實例對象共享,靜態成員有靜態成員變量和靜態成員函數,靜態成員變量使用前必須初始化,靜態成員變量可以被靜態成員函數和非靜態成員函數訪問,而靜態成員函數只能訪問靜態成員變量,因為靜態成員函數屬于類,其沒有this指針。非靜態成員每個對象都有一個。
8、純虛函數的定義?
答:virtual void fun()=0;含有純虛函數的類為抽象類,抽象類不能實例化對象,但是可以定義指針,純虛函數是接口,由子類實現。
9、請講一講析構函數和虛函數的用法和作用?
答:析構函數是用于在撤銷對象時完成對對象的清理工作,比如在創建對象時,如果在構造函數中動態申請了內存,那么在對象釋放時,應該在析構函數中對動態申請的內存進行釋放,避免造成內存泄露,如果在這個時候還不釋放就沒有機會釋放內存了,會造成內存泄露,總之需要在釋放對象之前完成的工作都可以放在析構函數中完成,析構函數不需要用戶顯示調用,它會在釋放對象前由系統自動調用。虛函數是實現多態性的方式,虛函數在子類中被重寫,因此可以通過基類指針調用不同子類的虛函數,實現一個接口,多種實現。正是因為多態性的存在,為了使子類的析構函數隨時都能夠執行,基類的析構函數一般都聲明為虛析構函數。
10、全局變量和局部變量有什么區別?是怎么實現的?操作系統和編譯器是怎么知道的?
答:區別在于它們的作用域不同,全局變量可以在整個程序被使用,局部變量只能在子程序或函數中使用,函數執行完后,局部變量的也被銷毀了。操作系統和編譯器可能是通過它們所分配的內存區來知道的,全局變量被放在全局數據區,而局部變量放在堆棧中。
11、若類A和類B沒有繼承關系,對于函數void func(A&),請至少用兩種不同方法說明如何才能傳遞一個非常量的B類對象非func函數。
答:可在A類中定義一個構造函數 A(const B&)
? ?? ? 或在B類中定義一個自動轉換函數:operator A() const
12、static全局變量與普通的全局變量有什么區別?static局部變量和普通局部變量有什么區別?static函數與普通函數有什么區別?
答:static全局變量與普通的全局變量的區別:前者在主函數之前就要被初始化,而后者沒有要求,兩者的作用域不同,前者的作用域只限于子模塊(子程序或函數),而后者在整個程序中都可以被使用。
? ?? ? static局部變量和普通局部變量的區別:一個函數中的static變量值會保留到該函數下次調用來改變它,而后者在函數運行完后就被銷毀了,兩者的存儲區域不同,前者存儲在靜態區(全局區),后者的內存位于堆棧上。
? ?? ? static函數與普通函數:static函數可以直接通過類調用,不需要在此之前實例化對象,而普通函數需要先定義對象。static函數不能用非static成員。static在循環中定義并賦值時,定義過程只進行一次,而不是每個循環1次。
13、寫程序,從鍵盤上輸入一系列整數,以輸入-1為結束,將輸入的數據保存到文件data.txt中。
答:C語言實現:
#include <stdio.h>
int main()
{
? ?? ? FILE *fp;
? ?? ? if ((fp=fopen("data.txt","wb")) == NULL)
? ?? ? {
? ?? ?? ?? ???cout<<"open error"<<endl;
? ?? ?? ?? ???exit(1);
? ?? ? }
? ?? ? int x;
? ?? ? scanf(“%d”, &x);
? ?? ? while (x!=-1)
? ?? ? {
? ?? ?? ?? ???fputc(x, fp);
? ?? ?? ?? ???cin>>x;
? ?? ? }
? ?? ? fclose(fp);
? ?? ? return 0;
}
C++實現:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
? ?? ? ofstream fout("data1.txt");
? ?? ? if (!fout)
? ?? ? {
? ?? ?? ?? ???exit(1);
? ?? ? }
? ?? ? int x;
? ?? ? cin>>x;
? ?? ? while (x != -1)
? ?? ? {
? ?? ?? ?? ???fout<<x<<' ';
? ?? ?? ?? ???cin>>x;
? ?? ? }
? ?? ? fout.close();
? ?? ? return 0;
}
14、寫程序,將一個字符串倒序?
答:直接在main函數中實現的
void main()
{
char *source = "hello";
? ?? ? char *des;
? ?? ? int len = strlen(source);
? ?? ? des = (char *)malloc(len+1);??//申請空間必須是len+1,加1是為了放結束符
? ?? ? if (!des)
? ?? ? {
? ?? ?? ?? ???exit(1);
? ?? ? }
? ?? ? char *s = &source[len-1];
? ?? ? char *d = des;
? ?? ? while (len--!=0)
? ?? ? {
? ?? ?? ?? ???*d++ = *s--;
? ?? ? }
? ?? ? *d = '\0';??//必須要
? ?? ? cout<<source<<endl;
? ?? ? cout<<des<<endl;
}
15、static的用途?
答:(1)函數體內的靜態變量,其值在函數的調用過程中保持不變。跟局部變量的區別。
? ?(2)在函數體外定義的靜態變量,限制了它的使用范圍只在于該子模塊,該子模塊內的函數都能訪問它,但是子模塊外不能訪問,實際就類似于是一個本地的全局變量。與一般全局變量的區別。
??(3)類的靜態成員函數。
? ?? ? 本質上來說,static就是聲明了對象的生成期,限制了對象的作用域。
或 (1)函數體內static變量的作用范圍為該函數體,不同于auto變量,該變量的內存只能被分配一次,因此其值在下次函數調用時仍維持上次的值。
? ?(2)在模塊內的static全局變量可以被模塊內的所有函數訪問,但不能被模塊外其他函數訪問。
? ?(3)在模塊內的static函數只可被這一模塊內的其他函數調用,這個函數的使用范圍被限制在聲明它的模塊。
? ?(4)在類中的static成員變量屬于整個類所有,對類的所有對象只有一份拷貝。
? ?(5)在類中的static成員函數屬于整個類所有,這個函數不接受this指針,因而只能訪問類的static成員變量。
16、在C++程序中調用C編譯后的函數,為什么要加extern C的聲明?
答:因為C++支持函數重載,而C不支持函數重載。函數被C++編譯后在庫中的名字與C語言的不同。假設某個函數的原型為:void foo(int x, int y);該函數被C編譯器編譯后在庫中的名字為_foo,而C++編譯器則產生像_foo_int_int之類的名字。C++提供了C連接交換指定符號 extern C來解決名字匹配問題。
17、float x與零值的比較,指針p與NULL的比較,布爾變量flag與TRUE的比較
答:(1)const float EPSION = 0.00001;
? ?? ?? ?? ???if (x >= -EPSION && x <= EPSION)
? ?? ? (2) if (p == NULL)
? ?? ? (3) if (flag)
18、C++中哪些函數不能被聲明為虛函數?
答:普通函數(非成員函數),構造函數,內聯成員函數、靜態成員函數、友元函數。
(1)虛函數用于基類和派生類,普通函數所以不能
(2)構造函數不能是因為虛函數采用的是虛調用的方法,允許在只知道部分信息的情況的工作機制,特別允許調用只知道接口而不知道對象的準確類型的方法,但是調用構造函數即使要創建一個對象,那勢必要知道對象的準確類型。
(3)內聯成員函數的實質是在調用的地方直接將代碼擴展開
(4)繼承時,靜態成員函數是不能被繼承的,它只屬于一個類,因為也不存在動態聯編等
(5)友元函數不是類的成員函數,因此也不能被繼承
19、include <filename.h>和include “filename.h”的區別?
答:<>是從標準庫路徑搜索, “”是從用戶當前工作目錄開始,找不到,在到標準庫開始
20、void GetMemory(char **p, int num)
{??
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
請問運行Test 函數會有什么樣的結果?
答:輸出“hello”, 注意GetMemory函數中的p參數是指向指針的指針,如果是一級指針則出現錯誤,不會得到申請的地址空間。
21.void GetMemory(char *p, int num)
{??
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100);
strcpy(str, "hello");
printf(str);
}
請問運行Test 函數會有什么樣的結果?
答:運行出錯,注意與20中參數p的類型比較,20中是char **p,而本題是char *p。而且像這種情況還會導致每次都泄露一塊內存的危險。
22、char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
 請問運行Test 函數會有什么樣的結果?
 答:無效的指針,輸出不確定,因為p是函數GetMemory中申請的是一個臨時變量,函數調用完后,其空間已經不再存在。
23.char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
 請問運行Test 函數會有什么樣的結果?
 答:輸出”hello”,因為是返回動態申請的內存,只要不釋放內存,即調用free函數,就可以使用該段內存。
24.char *GetString2(void)
{
char *p = "hello world";
return p;
}
void Test5(void)
{
char *str = NULL;
str = GetString2();
cout<< str << endl;
}
答:函數Test5運行雖然不會出錯,但是函數GetString2的設計概念卻是錯誤的。因為GetString2內的“hello world”是常量字符串,位于靜態存儲區,它在程序生命期內恒定不變。無論什么時候調用GetString2,它返回的始終是同一個“只讀”的內存塊。如果此時還想利用strcpy往str中寫數據時,將出現錯誤。如strcpy(str,”hello world”)。
25、編寫strlen函數
答:
int Strlen(const char *str)
{
int len = 0;
? ?? ?? ?assert(str != NULL);
? ?? ?? ?while (*str++ != '\0')
? ?? ?? ?{
? ?? ?? ?? ?? ?? ? len++;
? ?? ?? ?}
? ?? ?? ?return len;}
非空判斷是必須進行的操作,可以使用斷言的方式assert(str) != NULL才會繼續
26、編寫strcpy函數
答:??char *StrCopy(char *strDes, const char *strSrc)
{
? ?? ? assert((strDes != NULL) && (strSrc != NULL));??
? ?? ? char *address = strDes;
? ?? ? while ((*strDes++ = *strSrc++) != '\0')
? ?? ?? ?? ?? ? ;
? ?? ? return address;
}
首先必須判斷兩個指針是否為空,由于復制后的指針需要返回,因此需要一個指針來記錄地址的初始值,最后將復制的結果返回是為了進行鏈式操作。
27、C語言的關鍵字
28、什么是定義?什么是聲明?它們的區別?
答:定義創建了對象并為這個對象分配了內存,聲明沒有分配內存。
29、最寬宏大量的關鍵字 auto
? ?? ? 最快的關鍵字 register
? ?? ? 最名不副實的關鍵字static,其作用包括,定義靜態全局變量,靜態局部變量,修飾函數限制函數的作用域為該函數所在的文件,其他文件不能訪問。
30、字符串與整數之間的轉換。
31、字符串循環右移n位。
32、棧的生長方向是向低地址擴展,而堆是向高地址擴展。
33、什么是“引用”?申明和使用“引用”要注意哪些問題?
答:引用就是某個目標變量的“別名”(alias),對引用的操作與對變量直接操作效果完全相同。申明一個引用的時候,切記要對其進行初始化。引用聲明完畢后,相當于目標變量名有兩個名稱,即該目標原名稱和引用名,不能再把該引用名作為其他變量名的別名。聲明一個引用,不是新定義了一個變量,它只表示該引用名是目標變量名的一個別名,它本身不是一種數據類型,因此引用本身不占存儲單元,系統也不給引用分配存儲單元。不能建立數組的引用。
34、將“引用”作為函數參數有哪些特點?
答:(1)傳遞引用給函數與傳遞指針的效果是一樣的。這時,被調函數的形參就成為原來主調函數中的實參變量或對象的一個別名來使用,所以在被調函數中對形參變量的操作就是對其相應的目標對象(在主調函數中)的操作。
(2)使用引用傳遞函數的參數,在內存中并沒有產生實參的副本,它是直接對實參操作;而使用一般變量傳遞函數的參數,當發生函數調用時,需要給形參分配存儲單元,形參變量是實參變量的副本;如果傳遞的是對象,還將調用拷貝構造函數。因此,當參數傳遞的數據較大時,用引用比用一般變量傳遞參數的效率和所占空間都好。
(3)使用指針作為函數的參數雖然也能達到與使用引用的效果,但是,在被調函數中同樣要給形參分配存儲單元,且需要重復使用"*指針變量名"的形式進行運算,這很容易產生錯誤且程序的閱讀性較差;另一方面,在主調函數的調用點處,必須用變量的地址作為實參。而引用更容易使用,更清晰。
35、在什么時候需要使用“常引用”?
答:如果既要利用引用提高程序的效率,又要保護傳遞給函數的數據不在函數中被改變,就應使用常引用。常引用聲明方式:const 類型標識符 &引用名=目標變量名。
36、Heap和Stack的區別?
答:Heap是堆,Stack是棧。
? ?? ? 棧的空間由操作系統自動分配和回收,而堆上的空間由程序員申請和釋放。
? ?? ? 棧的空間大小較小,而堆的空間較大。
? ?? ? 棧的地址空間往低地址方向生長,而堆向高地址方向生長。
? ?? ? 棧的存取效率更高。
? ?? ? 程序在編譯期間對變量和函數的內存分配都在棧上,且程序運行過程中對函數調用中參數的內存分配也是在棧上。
37、宏定義,將整形變量a的第n位清零,其他位不變。
答:#define clear_bit(a,n) a=((a|(int)pow(2,n-1))!=a)?aa^(int)pow(2,n-1))
? ?? ? 首先判斷數a的第n位本身是否為零,如果為零,則與2的n-1次方或運算后肯定不等于數a,此時就不需要清零,直接返回a即可,否則,將數a與2的n-1次方進行異或運算。
38、unsigned short A = 10;
? ?? ? printf(“%u\n”, ~A);
? ?? ? char ch = 128;
? ?? ? printf(“%d\n”, ch);
? ?? ? 輸出的結果是多少,并分析過程?
答:~A=4294967285,首先將A轉化為int類型,即對應的二進制數值為:00000000 00000000 00000000 00001010,~A=11111111 11111111 11111111 11110101,其實這種情況最高位是1,認為是負數,但是在輸出中指定以無符號數輸出,于是結果為4294967285=4294967295(四字節表示的最大數)-10.
ch = 128對應的二進制為:10000000,在輸出中以整數形式輸出,由于最高位是1,于是就是負數,10000000是該負數的補碼,根據求補碼的反步驟計算,先-1,得到01111111,在取反得10000000=128,由于本身是負數,即為-128.
38、sizeof和strlen之間的區別?
答:(1)sizeof操作符的結果類型是size_t,它在頭文件中的typedef為unsigned int類型,該類型保證能容納實現所建立的最大對象的字節大小。
? ?? ? (2)sizeof是運算符,strlen是函數
? ?? ? (3)sizeof可以用類型做參數,strlen只能用char *做參數,且必須是以’\0’結尾的。
? ?? ? (4)數組做sizeof的參數不退化,傳遞給strlen就退化為指針。
? ?? ? (5)大部分編譯程序在編譯的時候就把sizeof計算過了,是類型或是變量的長度。
? ?? ? (6)strlen的結果要在運行的時候才能計算出來,用來計算字符串的長度,而不是類型占用內存的大小。
? ?? ? (7)sizeof后如果是類型必須加括號,如果是變量名可以不加括號。
? ?? ? (8)當使用了一個結構類型或變量時,sizeof返回實際的大小。
? ?? ? (9)數組作為參數傳遞給函數時傳的是指針而不是數組,傳遞的是數組的首地址。
? ?? ? (10)計算結構變量的大小就必須討論數組對齊問題。
? ?? ? (11)sizeof操作符不能用于函數類型,不完全類型或位字段。
39、sizeof的使用場合?
答:(1)sizeof操作符的一個主要用途是與存儲分配和I/O系統那樣的例程進行通信。
? ?? ? (2)用它可以看看某種類型的對象在內存中所占的單元字節。
? ?? ? (3)在動態分配一對象時,可以讓系統知道要分配多少內存。
? ?? ? (4)便于一些類型的擴充。
? ?? ? (5)由于操作數的字節數在實現時可能出現變化,建議在涉及到操作數字節大小時用sizeof代替常量計算。
? ?? ? (6)如果操作數是函數中的數組形參或函數類型的形參,sizeof給出其指針的大小。
40、內聯函數和宏的差別?
答:內聯函數和普通函數相比可以加快程序運行的速度,因為不需要中斷調用,在編譯的時候內聯函數可以直接被鑲嵌到目標代碼中,而宏只是一個簡單的替換。內聯函數要做參數類型檢查,這是與宏相比的優勢。
? ?? ? Inline是指嵌入代碼,就是在調用函數的地方不是跳轉,而是把代碼直接寫到那里去。對于短小的代碼來說,inline可以帶來一定效率的提升,而且和C時代的宏函數相比,inline更安全可靠。可是這是以增加空間消耗為代價的。
? ?? ? Inline一般只適用于:一個函數被不斷地重復調用;函數只有簡單的幾行,且函數內不能含有for while switch語句。
41、文件操作函數?
答:FILE *fp
? ?? ? fopen(“filename”, “rwatb+”)
? ?? ? char ch? ? ch = fgetc(fp)? ?? ???fputc(ch, fp)
? ?? ? char str[n]? ?? ?fgets(str, n, fp)? ?? ?? ?? ???fputs(str, fp)
? ?? ? fread(buffer, size, count, fp)? ?? ?? ?fwrite(buffer, size, count, fp)
? ?? ? fscanf(fp, “%c”, &ch)? ?? ?? ???fprintf(fp, “%d”, ch)
? ?? ? fclose(fp)
? ?? ? rewind(fp)
? ?? ? fseek(fp, 偏移長度,SEEK_SET/SEEK_CUR/SEEK_END)
feof(fp)
總結
以上是生活随笔為你收集整理的C/C++面试题分享的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 交互两个数(不引入第三个变量)
- 下一篇: 一道题弄明白二维数组的指针
