c++面試題中經常被面試官面試的小問題總結(二)(本篇偏向指針知識)


 

原文作者:aircraft

原文鏈接:https://www.cnblogs.com/DOMLX/p/10713204.html

 

 

一套面試題的目錄在此,還在繼續完善中。。。。。。

c/c++ 2019面試題目錄

 

1.利用指針交換兩個字符串方法?(這題是我當年讀大一的時候看到的,好懷念!!!QAQ)

(一)指針引用

#include<iostream>
using namespace std; void swap(char *&a,char *&b) { char *temp; temp = a; a = b; b = temp; } int main() { char *ap = "hello"; char *bp = "word"; swap(ap,bp); cout<<"ap:"<<ap<<endl; cout<<"bp:"<<bp<<endl; return 0; }

(二)二維指針指向一維

#include<iostream>
using namespace std; void swap(char **a,char **b) { char *temp; temp = *a; *a = *b; *b = temp; } int main() { char *ap = "hello"; char *bp = "word"; swap(&ap,&bp); cout<<"ap:"<<ap<<endl; cout<<"bp:"<<bp<<endl; return 0; }

 

2.參數引用--查找下面程序錯誤

#include<iostream>
using namespace std; const float pi = 3.14f; float f; float f1(float r) { f = r*r*pi; return f; } float &f2(float r) { f = r*r*pi; return f; } int main() { float f1(float=5); float &f2(float=5); float a = f1(); float &b = f1(); //雖然返回的好像是一個全局變量,但是函數在處理的時候 //編譯器機制返回的依然是一個臨時建立的temp變量里面存放的是f內的值,對其進行引用報錯 
    float c = f2(); float &d = f2();//函數定義返回值的時候加了引用,此時不會生成臨時變量 //直接返回全局變量f,這種定義最節省空間,但是要注意全局變量f生存周期要大於引用d //這里是安全的
 d += 1.0f; cout<<"a:"<<a<<endl; cout<<"b:"<<b<<endl; cout<<"c:"<<c<<endl; cout<<"d:"<<d<<endl; cout<<"f:"<<f<<endl; return 0; }   

 

3.下面輸出是什么?

 int a[5]={1,2,3,4,5}; int *ptr=(int *)(&a+1); printf("%d,%d/n",*(a+1),*(ptr-1));

答案:輸出:2,5
  *(a+1)就是a[1],*(ptr-1)就是a[4],執行結果是2,5
  &a+1不是首地址+1,系統會認為加一個a數組的偏移,是偏移了一個數組的大小(本例是5個int)
  &a是數組指針,其類型為 int (*)[5];

 

4.復雜的指針聲明

簡單級別:

a-一個整型數

b-一個指向整型數的指針

c-一個指向指針的指針,它指向的指針是指向一個整型數的

d-一個有十個整型數的數組

e-一個有十個指針的數組,該指針指向一個整型數

f-一個指向十個整型數數組的指針

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

答案:

a: int a; b: int *a; c: int **a; d: int a[10]; e: int *a[10]; f: int (*a)[10]; g: int (*a)(int);

復雜級別:

a-一個有十個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數

b-func是一個指向數組的指針,這個數組的元素是函數指針,這些指針指向有int*類型的形參,返回值為Int類型的函數

c-func是一個函數指針,這類函數具有int*類型的形參,返回值是指向數組的指針,所指向的元素是5個int元素的數組

答案:

a: int (*a[10])(int); b: int (*(func)[5])(int *p); c: int(*(func)(int *p))[5];

 

5.指針數組與數組指針(這個大一剛學的時候真的很混亂!!)

(不管是數組指針還是指針數組,像這像的詞前半區都是修飾詞,修辭后面,數組指針本質是個指針,指向一個數組。指針數組本質是個數組,數組里存放的是指針)

---同理:指針常量和常量指針。函數指針和指針函數都可以這么理解

 

數組指針(也稱行指針)
定義 int (*p)[n];
()優先級高,首先說明p的本質是一個指針,指向一個整型的一維數組,這個一維數組的長度是n,也可以說是p的步長。也就是說執行p+1時,p要跨過n個整型數據的長度。

如要將二維數組賦給一指針,應這樣賦值:
int a[3][4];
int (*p)[4]; //該語句是定義一個數組指針,指向含4個元素的一維數組。
 p=a;        //將該二維數組的首地址賦給p,也就是a[0]或&a[0][0]
 p++;       //該語句執行過后,也就是p=p+1;p跨過行a[0][]指向了行a[1][]

所以數組指針也稱指向一維數組的指針,亦稱行指針。

 

指針數組
定義 int *p[n];
[]優先級高,先與p結合成為一個數組,再由int*說明這是一個整型指針數組,它有n個指針類型的數組元素。這里執行p+1時,則p指向下一個數組元素,這樣賦值是錯誤的:p=a;因為p是個不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它們分別是指針變量可以用來存放變量地址。但可以這樣 *p=a; 這里*p表示指針數組第一個元素的值,a的首地址的值。
如要將二維數組賦給一指針數組:
int *p[3];
int a[3][4];
p++; //該語句表示p數組指向下一個數組元素。注:此數組每一個元素都是一個指針
for(i=0;i<3;i++)
p[i]=a[i]
這里int *p[3] 表示一個一維數組內存放着三個指針變量,分別是p[0]、p[1]、p[2]
所以要分別賦值。

這樣兩者的區別就豁然開朗了,數組指針只是一個指針變量,似乎是C語言里專門用來指向二維數組的,它占有內存中一個指針的存儲空間。指針數組是多個指針變量,以數組形式存在內存當中,占有多個指針的存儲空間。
還需要說明的一點就是,同時用來指向二維數組時,其引用和用數組名引用都是一樣的。
比如要表示數組中i行j列一個元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

優先級:()>[]>*

 

6.下面輸出的是什么?

int main() { char * str[] = {"Welcome","to","Forteedia","Nanjing"} ; char **p = str + 1; str[0] = (*p++) +2; str[1] = *(p+1); str[2] = p[1] + 3 ; str[3] = p[0] + (str[2] - str[1]); printf("%s\n",str[0]); printf("%s\n",str[1]); printf("%s\n",str[2]); printf("%s\n",str[3]); return 0; }
答案:
(空)
Nanjing
jing
g

其他應該沒有疑問就說一下第一個空 。
char **p = str + 1; 這句指向“to”
str[0] = (*p++) +2;這里(*p++),p本身為一個二維指針,*號已經是指向一維。++的優先級高於*,意思就是*(p++)
但是后置++先執行完此行代碼在自加的 先不管
這里如果是*p+1的話那么str【0】輸出的是“o”,
也就是*p指向“to”的t,*p+1指向o,*p+2指向“to”之后的字符串結尾符“\0”,為空
代碼邏輯表示為str[1][2];而“to”這里只有str[1][0],str[1][1];
好了執行完畢 后置++運行 p=p+1;指向"Forteedia"也就是str[2] 這就是后面str[3] = g;的伏筆

覺得自己完全理解這題的指針知識點了嗎,那么p[1]+3這行執行完畢p[0]為什么指向‘j’了呢?
可以在評論區寫出你的回答,也可以關注我,下篇做出解釋,hhhhh

答案就是 前面的伏筆 p的指向str[2],修改str[2] = p[1] + 3 ; 就是修改 p=p[1] + 3 ;
所以p[0]就是“jing”的首地址 (str[2] - str[1])這個等於3不用說了吧 N到j的地址距離

 

 

7.代碼改錯-函數指針的使用(下面代碼有什么問題?打印三個數中最大者)

#include<iostream>
using namespace std; int max(int x,int y) { return x>y?x:y; } int main() { int *p; int a,b,c; int result; int max(x,y); p=max; cout<<"please input three integer"<<endl; cin>>a>>b>>c; result = (*p)((*p)(a,b),c); cout<<"result= "<<relust<<endl; return 0; }
答案: #include<iostream>
using namespace std; int max(int x,int y) { return x>y?x:y; } int main() { int (*p)(int,int); //改正 定義一個函數指針,才能指向一個函數
    int a,b,c; int result; int max(int,int);  //改正 聲明函數是寫形參的類型
 p=&max;            
    cout<<"please input three integer"<<endl; cin>>a>>b>>c; result = (*p)((*p)(a,b),c); cout<<"result= "<<result<<endl; return 0; }

 

8.typedef用於函數指針定義

下面的定義有什么作用?

typedef int (*pfun)(int,int);

這里的pfun是一個使用typedef的自定義數據類型。意思就是:定義了一種pfun的類型,並定義這種類型為指向某種函數的指針,這種函數以兩個個int為參數並返回int類型。

這樣的話定義函數指針什么的就很方便了。

使用方法:

#include<iostream>
using namespace std; typedef int (*pfun)(int,int); int fun(int x,int y) { return (x+y); } int main() { int fun(int,int); pfun p = fun; //注意 pfun是類型 類型定義變量 int ret = p(2,3); cout<<ret<<endl; return 0; }

 

9.什么是“野指針”?

“野指針”不是NULL指針,而是指向”垃圾”內存的指針。其成因主要為:指針變量沒有被初始化,或者指針p被free或者delete之后沒有置為NULL

 

10.有了malloc/free為什么還要new 和delete

  mallocfreeC++/C語言的標准庫函數,new/deleteC++的運算符。它們都可用於申請動態內存和釋放內存。

對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free

       因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,以及一個能完成清理與釋放內存工作的運算符delete。注意new/delete不是庫函數。

我們先看一看malloc/freenew/delete如何實現對象的動態內存管理,見下列代碼

 

class Obj
{
    public :
    Obj(void){ cout << "Initialization" << endl; }
    ~Obj(void){ cout << "Destroy" << endl; }
    void    Initialize(void){ cout << "Initialization" << endl; }
    void    Destroy(void){ cout << "Destroy" << endl; }
};

void UseMallocFree(void)
{
    cout<<"use mallocFree。。"<<endl;
    Obj *a = (obj *)malloc(sizeof(obj));   // 申請動態內存
    free(a);        // 釋放內存
}

void UseNewDelete(void)
{
    cout<<"use newFree。。"<<endl;
    Obj *a = new Obj; // 申請動態內存並且初始化

    delete a;           // 清除並且釋放內存
} 
int main()
{
    UseMallocFree();   
    UseNewDelete();

    return 0;
}
    

 

打印結果如下:

use mallocFree。。

 

use newFree。。

Initialization

Destroy

 

對於非內部數據類型的對象而言,對象在消亡之前要自動執行析構函數。由於malloc/free是庫函數而不是運算符,不在編譯器的控制權限之內,不能把執行構造函數和析構函數的任務強加於malloc/free,因此只有使用new/delete運算符

 

11.比較分析兩個代碼段的輸出,錯誤點在哪--(動態內存的傳遞)

代碼段一

char * getMemory()
{
    char p[] = "hello";
    return p;   
}

void Test(void)
{
    char *str = NULL;
    str = getMemory();
    printf(str);
}

 

代碼段二

void getMemory(char * p)
{
    p = (char*)malloc(100);
}

void Test(void)
{
    char *str = NULL;
    getMemory(str);
    strcpy(str,"hello");
    printf(str);
}

代碼段一:棧內存分配,函數結束自動銷毀,輸出亂碼。

代碼段二:此時的函數形參只是個復制體,不能傳遞動態內存給實參。並且函數結束后丟失堆內存地址,不能釋放,導致內存泄漏。

 

 

 

本篇是第二篇面試題總結,后面還有好多篇,想要劍指offer的關注我把!!!

 

若有興趣交流分享技術,可關注本人公眾號,里面會不定期的分享各種編程教程,和共享源碼,諸如研究分享關於c/c++,python,前端,后端,opencv,halcon,opengl,機器學習深度學習之類有關於基礎編程,圖像處理和機器視覺開發的知識


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM