面試題1:什麼是引用?引用有什麼作用?
答案:引用就是一個目标變量的别名,對引用的一切操作和對變量的直接操作是一樣的。主要用作函數的參數、
函數傳回值和常引用。
面試題2:簡述為什麼引入常引用,常引用有什麼作用?
答案:常引用的引入主要是為了避免使用變量的引用時,在不知情的情況下改變變量的值。常引用主要用于定義
一個普通變量的隻讀屬性的别名、作為函數的傳入形參,避免實參在調用函數中被以外地改變。
面試題3:流操作符重載為什麼傳回引用?
答案:在程式中,流操作符<<和>>經常連續使用。是以這兩個操作符的傳回值應該是一個仍舊支援這兩個操
作符的流引用。其他的資料類型都無法做到這一點。
面試題4:說明以下聲明的含義
A: int(**p)[10];
B: char*(*p)[10];
C: float(*p[10])();
D: double*((*p)[10]);
E: short(*p)(char);
F: long(*(*p)(int,int)(int)
答案:
A: p為數組指針的指針,數組有10個元素,元素為int型資料。
B: p為數組的指針,數組有10個元素,元素為char*型資料。
C: p為數組,數組有10個元素,元素為函數指針類型,這個函數沒有參數,傳回值為float型資料。
D: p為數組的指針,數組有10個元素,元素為double*型資料。
E: p為函數指針,它所指向的函數有一個char型的參數,傳回值為short型資料。
F: p為函數指針,它所指向的函數有兩個int型的參數,傳回值為函數指針型資料,這個傳回的函數指針指向
的函數有一個int型的參數,傳回值為long型資料。
面試題5:簡述指針常量與常量指針的差別
什麼是指針常量?什麼是常量指針?兩者有什麼差別?
答案:指針常量是定義了一個指針,這個指針的值隻能在定義時初始化,在其他地方不能改變。常量指針是指定
義了一個指針,這個指針指向一個隻讀的對象,不能通過常量指針來改變這個對象的值。
指針常量強調的是指針的不可改變性,而常量指針強調的是指針對其所指對象的不可改變性。
面試題6:寫出以下代碼的輸出結果:
#include <iostream.h>
void main(void)
{
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout<<(str1 == str2)<<endl;
cout<<(str3 == str4)<<endl;
cout<<(str5 == str6)<<endl;
cout<<(str7 == str8)<<endl;
}
答案:0 0 1 1
運作結果:
1
1
面試題7:找出代碼的錯誤并改正。
#include <stdio.h>
void swapxy(char *a,char *b)
{
char x = *a, y = *b;
x = x + y;
y = x - y;
x = x - y;
*a = x, *b = y;
return;
}
void main(void)
{
char a = 'a', b = 'b';
char &x = a; &y = b;
printf("a = %c and b = %c before swap.\n",a,b);
swapxy(x,y);
printf("a = %c and b = %c after swap.\n",a,b);
return;
}
答案:
#include <stdio.h>
void swapxy(char &a, char &b)
{
char x = a, y = b;
x = x + y;
y = x - y;
x = x - y;
a = x, b = y;
return;
}
void main(void)
{
char a = 'a', b = 'b';
char &x = a, &y = b;
printf("a = %c and b = %c before swap.\n",a,b);
swapxy(x,y);
printf("a = %c and b = %c after swap.\n",a,b);
return;
}
運作結果:
a = a and b = b before swap.
a = b and b = a after swap.
面試題8:寫出代碼的輸出結果
#include <iostream.h>
void main(void)
{
int b = 0;
int a[5] = {0,1,2,3,4};
for(int i = 0, i < 5)
{
i = a[i+1];
cout<<a[i]<<endl;
}
return;
}
答案:輸出結果為:1 2 3 4 0 1 2 3 4 0 1 2 3 4……
(注意結果是一個死循環,重複輸出1 2 3 4 0)
面試題9:下面這段程式有問題嗎?如果沒有,請寫出列印結果。
#define MAX 255
# include <stdio.h>
void main(void)
{
unsigned char str[MAX],i;
for( i = 0; i <= MAX; i++)
{
str[i] = i;
printf("%d\n",str[i]);
}
}
答案:這個程式是死循環加數組越界通路(C/C++不進行數組越界檢查)。
面試題10:a和&a有什麼差別
請寫出以下代碼的列印結果。
# include <stdio.h>
void main(void)
{
int a[5] = {1,2,3,4,5};
int *ptr = (int*)(&a+1);
printf("%d,%d\n",*(a+1),*(ptr-1));
return;
}
答案:2,5
運作結果:
2,5
解析:*(a+1)就是a[1],但是*(ptr-1)并不是a[0],因為&a+1并不是數組a的首位址加1,
系統會認為是數組a的首位址加上一個數組a的偏移,即偏移了一個數組的大小,(本例
中是5個int的位址)。原式int*ptr(int*)(&a+1);的結果是,ptr指向了數組的第六個元
素a[5]。可參考如下程式:
# include <stdio.h>
void main(void)
{
int a[5] = {1,2,3,4,5};
int *ptr = (int*)(&a+1);
printf("&a :%d\nptr:%d\n",&a,ptr);
return;
}
運作結果:
&a :1703724
ptr:1703744
從結果可以看出,ptr比數組a的首位址偏移了20個位元組的位址,即偏移了5個int型資料,原因是&a是
數組a的指針,其類型為int(*)[5],而指針加1要根據指針類型加上一定的值,不同類型的指針加1後
的結果不同。可參考如下代碼:
# include <stdio.h>
void main(void)
{
int i = 1;
char c = 2;
double f = 3.0;
char A[3] = {0,1,2};
int *pi = &i;
char *pc = &c;
double *pf = &f;
char (*pA)[3] = &A;
printf("&i:%d pi:%d pi+1:%d\n",&i,pi,pi + 1);
printf("&c:%d pc:%d pc+1:%d\n",&c,pc,pc + 1);
printf("&f:%d pf:%d pf+1:%d\n",&f,pf,pf + 1);
printf("&A:%d pA:%d pA+1:%d\n",&A,pA,pA + 1);
return;
}
運作結果:
&i:1703740 pi:1703740 pi+1:1703744
&c:1703736 pc:1703736 pc+1:1703737
&f:1703728 pf:1703728 pf+1:1703736
&A:1703724 pA:1703724 pA+1:1703727
面試題11:以下代碼有什麼問題?
#include <iostream.h>
void main(void)
{
char *p1,*p2;
char ch[12];
char **p;
p1 = ch;
pp = &ch;
p2 = *pp;
cout<<p1<<endl;
cout<<pp<<endl;
cout<<p2<<endl;
return;
}
答案:(1)數組ch沒有初始化。
(2)把數組指針指派給字元的二級指針。
修改方案:
#include <stdio.h>
void main(void)
{
char *p1;
char *p2;
char **pp;
char ch[] = "abcd";
printf("sizeof p1:%d,p2:%d,pp:%d,ch:%d\n",
sizeof(p1),sizeof(p2),sizeof(pp),sizeof(ch));
p1 = ch;
pp = &p1;
p2 = *pp;
if(p1 == p2)
{
printf("p1 equals p2\n");
}
else
{
printf("p1 doesn't equal p2\n");
}
return;
}
運作結果:
sizeof p1:4,p2:4,pp:4,ch:5
p1 equals p2
面試題12:數組名和指針的差別 請寫出以下代碼的列印結果:
#include <iostream.h>
#include <string.h>
void main(void)
{
char str[13] = "Hello world!";
char *pStr = "Hello world!";
cout<<sizeof(str)<<endl;
cout<<sizeof(pStr)<<endl;
cout<<strlen(str)<<endl;
cout<<strlen(pStr)<<endl;
return;
}
答案:
13
4
12
12
面試題13:請解析(*(void(*)())0)()的含義
請解析下面這條語句:
(*(void(*)())0)();
答案:從頭到尾解析結果如下
(1)void(*0)():是一個傳回值為void,參數為空的函數指針0。
(2)(void(*)())0:把0轉變成一個傳回值為void,參數為空的函數指針。
(3)*(void(*)())0:在上句的基礎上加*表示整個是一個傳回值為void,無參數,并且起始
位址為0的函數的名字。
(4)(*(void(*)()0)(): 這就是上句的函數名所對應的函數的調用。
面試題14:指出程式的錯誤
指出下面這段程式的錯誤并改正:
#include<stdio.h>
void main()
{
int max(x,y);
int *p = max;
int a,b,c,d;
scanf("%d,%d,%d",a,b,c);
d = p(p(a,b),c);
printf("d:%d\n",d);
return;
}
int max(int x,int y)
{
return(x > y ? x:y);
}
答案:
(1)int max(x,y); 函數聲明錯誤,改為:int max(int,int);
(2)int *p=max; 指針定義錯誤改為:int(*p)(int,int)=max;
(3)scanf("%d%d%d",a,b,c); 庫函數使用錯誤,改為scanf("%d%d%d",&a,&b,&c);
(4)d=p(p(a,b),c); 函數指針調用函數錯誤,改為d=(*p)((*p)(a,b),c);
修改後:
#include<stdio.h>
void main()
{
int max(int,int);
int (*p)(int,int) = max;
int a,b,c,d;
scanf("%d %d %d",&a,&b,&c);
d = (*p)((*p)(a,b),c);
printf("d:%d\n",d);
return;
}
int max(int x,int y)
{
return(x > y ? x:y);
}
運作結果:
45
78
23
d:78
面試題15:如何避免“野指針”
“野指針”是如何産生的?應該如何避免?
答案:“野指針”産生原因及解決辦法如下:
(1)指針變量聲明時沒有被初始化。解決辦法:指針聲明時初始化,可以使具體的位址值,也可以讓它指向NULL。
(2)指針p被free或delete之後,沒有置為NULL。解決辦法:指針指向的記憶體空間被釋放後指針應該指向NULL。
(3)指針操作超越了變量的作用範圍。解決辦法:在變量的作用域結束前釋放掉變量的位址空間,并且讓指針指向NULL。
面試題16:程式是否正确
下面這段程式是否正确?如果正确,請寫出列印結果:
#include <iostream.h>
typedef int* pInt;
void swap(int *p1,int *p2)
{
int *p;
*p = *p1;
*p1 = *p2;
*p2 = *p;
}
void main(void)
{
int a = 1, b = 3;
pInt p1 = &a, p2 = &b;
cout<<"a:"<<*p1<<" b:"<<*p2<<endl;
swap(p1,p2);
cout<<"a:"<<*p1<<" b:"<<*p2<<endl;
return;
}
答案:不正确,指針p沒有被初始化就直接使用,其為“野指針”會導緻不可預知的錯誤。
修改後:
#include <iostream.h>
typedef int* pInt;
void swap(int *p1,int *p2)
{
int p;
p = *p1;
*p1 = *p2;
*p2 = p;
}
void main(void)
{
int a = 1, b = 3;
pInt p1 = &a, p2 = &b;
cout<<"a:"<<*p1<<" b:"<<*p2<<endl;
swap(p1,p2);
cout<<"a:"<<*p1<<" b:"<<*p2<<endl;
return;
}
運作結果:
a:1 b:3
a:3 b:1
面試題17:指出程式的錯誤
#include <stdio.h>
class A
{
public:
A()
{
number = 0;
};
void Fun1()
{
printf("In Fun1 number is:%d\n",number);
};
void Fun2()
{
printf("In Fun2\n");
}
public:
int number;
};
void f(A *p, int i);
void main(void)
{
A *p;
f(p,5);
if(p != NULL)
{
p->Fun1();
p->Fun2();
}
}
void f(A *p,int i)
{
if(p == NULL)
{
A a;
a.number = i;
p = &a;
}
}
答案:在主函數中調用函數f後,p是“野指針”,用p調用函數Fun1時,會出現記憶體錯誤,因為在Fun1
中用到了類A的成員變量number,而類的成員變量屬于類的成員對象,在類成員對象作用域外對其的所
有通路都是非法的。
面試題18:簡述C/C++程式編譯的記憶體配置設定情況
在C/C++中不同性質的資料在編譯時存放在不同的位置,請簡述C/C++程式編譯時的記憶體配置設定情況。
答案:
(1)棧區(stack):由編譯器自動配置設定釋放,存放為運作函數而配置設定的局部變量、函數參數、傳回資料、
傳回位址等。其操作方式類似于資料結構中的棧。
(2)堆區(heap): 一般有程式員配置設定釋放,若程式員不釋放,程式結束時可能由系統回收。配置設定方式
類似于連結清單。
(3)全局區(靜态區)(static): 存放全局變量、靜态資料、常量。程式結束後由系統釋放。
(4)文字常量區: 常量字元串就是放在這裡的。程式結束後由系統釋放。
(5)程式代碼區:存放函數體(類成員函數和全局函數)的二進制代碼。
面試題19:以下四段代碼中哪段沒有錯誤?
請看這四段代碼:
代碼一:
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str,"hello world");
printf(str);
}
代碼二:
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory()();
printf(str);
}
代碼三:
void GetMemory(char **p,int num)
{
*p = (char *)malloc(num);
}
void Test(void);
{
char *str = NULL;
GetMemory(&str,100);
strcpy(str,"hello");
printf(str);
}
代碼四:
void Test(void)
{
char **str = (char *)malloc(100);
strcpy(str,"hello");
free(str);
}
答案:四段代碼全有錯誤。
*代碼一:GetMemory(char *p)的參數為字元型指針,在這個函數修改參數p的值時并不能
真正修改實參的值。
%代碼二:其中的p[]數組是函數GetMemory中的局部變量,函數傳回後,p就被釋放掉,
str便指向了一段無用的記憶體區域。
*代碼三:沒有判斷動态記憶體申請是否成功而直接使用,沒有釋放動态申請的記憶體,造成
記憶體洩漏。
*代碼四:沒有判斷動态記憶體申請是否成功而直接使用,動态記憶體釋放後沒有将指針置空。