我整理了這篇指針的知識點,想必對你有用

指針和引用的區別

  1. 非空區別: 任何狀況下都不能使用指向空值的引用,一個引用必須老是指向某些對象。 指針能夠指向空。
  2. 合法性區別: 引用在使用以前不須要測試合法性,指針應該老是要被測試,防止其爲空
  3. 可修改性區別: 引用在初始化時指定對象,之後不能修改。

指針傳遞動態內存

例1: 程序測試後會有什麼結果?ios

#include<iostream>
#include<cstring>
using namespace std;

void getMemory(char*p, int num)
{
    p = (char*)malloc(sizeof(char) * num);
}

int main(int argc, const char* argv[])
{
    char *str = NULL;
    getMemory(str, 100);
    strcpy(str, "hello");
    return 0;
}

問題出如今getMemory裏,編譯器老是爲函數的每一個參數製做一個臨時副本。在本題中,p爲str的拷貝,p申請了一個新的內存空間,可是並無影響到str,str仍是NULL,再調用strcpy(), 則會使代碼崩潰,而且p申請的內存也一直沒用釋放,形成內存泄露。c++

正確的方法是把往getMemory內傳入str的地址。數組

#include<iostream>
#include<cstring>
using namespace std;

void getMemory(char**p, int num)
{
    *p = (char*)malloc(sizeof(char) * num);
}

int main(int argc, const char* argv[])
{
    char *str = NULL;
    getMemory(&str, 100);
    strcpy(str, "hello");
    cout << str << endl;
    return 0;
}

不過這樣寫有些麻煩,咱們能夠直接把申請好的內存返回。函數

#include<iostream>
#include<cstring>
using namespace std;

char* getMemory(int num)
{
    return (char*)malloc(sizeof(char) * num);
}

int main(int argc, const char* argv[])
{
    char *str = NULL;
    str = getMemory(100);
    strcpy(str, "hello");
    cout << str << endl;
    return 0;
}

例2: 這個函數有什麼問題?測試

char* strA()
{
    char str[] = "hello world";
    return str;
}

str裏存的地址是函數strA棧幀裏"hello world"的首地址,函數調用完成,臨時空間被重置。優化

若是要得到正確的函數,能夠這樣寫:this

char* strA()
{
    char *str = "hello world";
    return str;
}

首先要搞清楚char str[], char* str, spa

char str[] 分配的是一個局部數組,操作系統

char* str 分配的是一個指針遍歷,指針

局部數組是局部變量,它對應的是內存中的棧。指針變量是全局變量,它對應的是內存中的全局區域。

不過上述代碼只能c語言這麼寫,c++不容許

ISO C++ forbids converting a string constant to ‘char*’

能夠這麼寫:

char* strA()
{
    static char str[] = "hello world";
    return str;
}

例3: 下面代碼的輸出結果是什麼?

#include<iostream>
using namespace std;

class A
{
public:
    A() { m_a = 1; m_b = 2; }
    ~A(){}
    void fun() { printf("%d%d", m_a, m_b);}
private:
    int m_a;
    int m_b;
};

class B
{
public:
    B() {m_c = 3;}
    ~B();
    void fun() {printf("%d", m_c);}
private:
    int m_c;
};


int main(int argc, const char* argv[])
{
    A a;
    B *pb = (B*)(&a);
    pb->fun();
    return 0;
}

這道題的目的就是考察你對內存偏移的理解,

B* pb = (B*)(&a);, 這是一個野蠻的轉換,強制把a地址內容當作一個B類的對象,pb指向的是a類的內存空間, 把a類空間按照B類的結構來解讀。

函數指針

例1: 找出下列程序的錯誤

#include<iostream>
using namespace std;

int Max(int x, int y)
{
    return x > y ? x : y;
}

int main(int argc, char const *argv[])
{
    int *p = &Max;
    cout << p(2, 1) << endl;
    return 0;
}

這道程序提存在着函數指針的錯誤使用問題,正確的寫法爲: int (*p)(int, int) = &Max;

int *p p 是int 型的指針

int *p(int, int), p是一個函數,返回值爲int*

int (*p)(int, int), p是一個指針,指向函數的地址,函數的返回值爲int

例2: 下面的數據聲明都表明什麼?

float(**def)[10];
double*(*gh)[10];
double(*f[10])();
int*((*b)[10]);
long(*fun)(int)
int(*(*F)(int, int))(int)

答案以下:

float(**def)[10];  // def是二級指針,指向一級指針,一級指針指向數組,數組的大小爲10,數組元素類型爲float
double*(*gh)[10];  // gh是一級指針,指向一個數組,數組大小爲10,數組元素的類型爲 double*
double(*f[10])();  // f是一個數組,數組大小爲10,數組的元素類型爲指針,指針指向的類型爲 double() 的函數
int*((*b)[10]);  // b是一個指針,指向一個數組,數組的大小爲10,數組元素的類型爲int*
long(*fun)(int)  // fun是一個函數指針,指向 long(int) 型的函數
int(*(*F)(int, int))(int)  // F是一個指針,指向一個函數,函數的參數爲(int, int), 函數的返回值是一個指針,指向一個函數,函數的參數爲(int), 函數的返回值爲int

指針數組和數組指針

例1: 如下程序的輸出是什麼?

#include<iostream>
using namespace std;

int main(int argc, char const *argv[])
{
    int v[2][10] = {
        {1,2,3,4,5,6,7,8,9,10},
        {11,12,13,14,15,16,17,18,19,20},
    };
    int (*a)[10] = v;       // 數組指針是一個二級指針
    cout << a << endl;      // a是一個指針,指向 {1,2,3,4,5,6,7,8,9,10}
    cout << *a << endl;     // *a也是一個指針,指向 1 的地址
    cout << **a << endl;    // **a 取 1的值

    cout << a + 1 << endl;  // 指針向後偏移一個位置,這個位置的長度爲指針所指容量的大小,偏移後指向 {11,12,13,14,15,16,17,18,19,20}
    cout << *(a + 1) << endl;  // 和*a的原理是同樣的,指向11的地址
    cout << **(a + 1) << endl;  // 取11的值

    cout << *a + 1 << endl;  // *a指向1的地址,*a + 1, 指針向後偏移一個位置,*a指向的是int型的數據m, 向後偏移sizeof(int),指向2
    cout << *(*a+1) << endl;  // 取2
    return 0;
}

例2: 用變量a給出下面的定義

  1. 一個整型數。
  2. 一個指向整型數的指針。
  3. 一個指向指針的指針,它指向的指針是一個指向一個整型數
  4. 一個有10個整型數的數組
  5. 一個有10個指針的數組,該指針是指向一個整型數的
  6. 一個指向有10個整型數數組的指針
  7. 一個指向函數的指針,該函數有一個整型參數並返回一個整型數
  8. 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數

答案:

// 1
int a;
// 2
int *a;
// 3
int **a;
// 4
int a[10];
// 5
int *a[10];
// 6
int (*a)[10];
// 7
int (*a)(int);
// 8
int (*a[10])(int);

例3: 寫出以下程序片斷的輸出

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

答案:

#include<iostream>
#include<cstdio>
using namespace std;

int main(int argc, char const *argv[])
{
    int a[] = {1,2,3,4,5};
    int *ptr = (int*)(&a + 1);

    cout << a << endl;  // 數組名的一個指向數組元素的常量指針
    cout << &a << endl;  // &a 並非一個指向常量指針的指針,而是一個指向整個數組的指針

    // 如下兩行驗證以上的觀點
    cout << a + 1 << endl;
    cout << &a + 1 << endl;

    // 因此 a + 1 指向2,*(a+1) 爲2
    // &a + 1 應該指向5的下一個元素,ptr - 1 指向5
    printf("%d %d", *(a+1), *(ptr - 1));
    return 0;
}

迷途指針和野指針

迷途指針: 指針指向一個內存,這個內存會回收了,可是沒有對這個指針作處理,沒有將指針設爲空

野指針: 聲明瞭一個指針,沒有將指針初始化。

例1: 下面的程序輸出結果是什麼?

#include<iostream>
using namespace std;

int main(int argc, char const *argv[])
{
    char s1[] = "hello";
    char s2[] = "the";
    char s3[] = "world";
    char* a[] = {s1, s2, s3};
    char **pa = a;
    pa++;
    cout << *pa << endl;
    return 0;
}

a 是一個常量指針,指向數組的首地址,pa++, 向後挪一個指針大小,指向s2, 輸出 "the"

指針和句柄

句柄是一個整數,是操做系統在內存中維護的一個對象,內存物理地址列表的整數索引,由於操做系統在內存管理時常常會將當前空閒對象的內存釋放掉,當須要訪問時再從新提交到物理內存,因此對象的物理地址是變化的,不容許程序直接經過物理地址來訪問對象。 程序將想訪問的對象的句柄傳遞給系統,系統根據句柄檢索本身維護的對象列表就能知道程序想訪問的對象及其物理地址了。

句柄是一種指向指針的指針。 咱們知道,所謂指針是一種內存地址,應用程序啓動後,組成這個程序的各對象是駐留在內存的。若是簡單的理解,彷佛咱們只要知道內存的首地址,那麼就能夠隨時用這個地址訪問對象。 可是,若是真的這樣認爲,那麼就大錯特錯了,咱們知道,操做系統是一個以虛擬內存爲基礎的,存在換頁現象,對象唄移動意味着它的地址變化了,若是地址老是變化,咱們該怎麼尋找對象呢? 爲了解決這個問題,操做系統爲各應用程序騰出一些內存地址,用來專門登記各應用對象在內存中的地址變化,而登記的地址是不變的,操做系統移動對象後,將對象的地址告訴給句柄,經過句柄就能知道對象具體的位置了。

句柄--> 登記對象地址的地址 --> 對象的地址 --> 對象

程序每次從新啓動,系統不保證分配給這個程序的句柄仍是原來的句柄,就比如去電影院每次賣給咱們的都不是同一個座位。

this 指針

關於this指針,有這樣一段描述: 當你進入一個房子後,你能夠看見桌子,椅子,等,可是你看不到全貌了

對於一個類的實例來講,你能夠看到成員函數,成員變量,可是實例自己呢? this指針就是這樣一個指針,時時刻刻指向實例自己

  1. this 指針本質是一個函數參數,只是編譯器隱藏起來的,語法層面的參數,實際上,成員函數默認第一個參數爲 T* const this
  2. this 在成員函數的開始前構造,結束後清除。

a.func(10); 會被編譯器編譯成 A::func(&a, 10);, 看起來和靜態函數沒區別,不過,區別仍是有的。 編譯器一般會對this指針作一些優化,this指針的傳遞效率比較高,如VC一般是經過ecx寄存器傳遞。

  1. this指針並不佔用對象空間,this至關於非靜態成員函數的一個隱含的參數,不佔對象空間。
  2. this指針存放在何處? this指針會因編譯器不一樣而有不一樣的位置,多是堆,棧,也多是寄存器。
  3. this指針是如何傳遞給類中的函數的? 大多數編譯器是經過ecx寄存器傳遞this指針,事實上,這也是一個潛規則,一半來講,不一樣編譯器都會聽從一致的傳參原則,不然不一樣的編譯器產生的obj就沒法匹配了
  4. 咱們只有得到一個對象後,才能經過對象使用this指針,若是咱們知道對象this的位置,能夠直接使用嗎?

this指針只有在成員函數中才有定義。 所以,你得到一個對象後,也不能經過對象使用this指針,只有在成員函數內纔有this
掃描二維碼關注我

相關文章
相關標籤/搜索