reference用法 C++中指針變量和引用變量之間有什麼區別?




c++參照 (24)

我知道引用是語法糖,因此代碼更容易讀寫。

但有什麼區別?

以下答案和鏈接摘要:

  1. 指針可以重新分配任意次數,而綁定後無法重新分配引用。
  2. 指針可以指向任何地方( NULL ),而引用總是指向一個對象。
  3. 您無法使用指針獲取引用的地址。
  4. 沒有“參考算術”(但是您可以獲取引用所指向的對象的​​地址,並在其上執行指針算法,如&obj + 5 )。

澄清一個誤解:

C ++標準非常謹慎,以避免規定編譯器如何實現引用,但每個C ++編譯器都將引用實現為指針。 也就是說,聲明如下:

int &ri = i;

如果它沒有完全優化 ,則分配與指針相同的存儲量,並將i的地址放入該存儲器中。

因此,指針和引用都使用相同數量的內存。

作為基本規則,

  • 使用函數參數和返回類型中的引用來提供有用的自記錄接口。
  • 使用指針實現算法和數據結構。

有趣的讀物:


如果您不熟悉以抽像或甚至學術方式學習計算機語言,那麼語義上的差異可能會顯得深奧。

在最高級別,引用的想法是它們是透明的“別名”。您的計算機可能會使用一個地址來使它們工作,但您不應該擔心:您應該將它們視為現有對象的“另一個名稱”,並且語法反映了這一點。它們比指針更嚴格,因此當您要創建懸空引用時,編譯器可以更可靠地警告您,而不是在您創建懸空指針時。

除此之外,指針和引用之間當然存在一些實際差異。使用它們的語法明顯不同,你不能“重新定位”引用,引用虛無,或指向引用。


我使用引用,除非我需要以下任何一個:

  • 空指針可以用作標記值,通常是避免函數重載或使用bool的廉價方法。

  • 你可以對指針進行算術運算。例如,p += offset;


  1. 可以重新分配指針:

    int x = 5;
    int y = 6;
    int *p;
    p =  &x;
    p = &y;
    *p = 10;
    assert(x == 5);
    assert(y == 10);
    

    引用不能,必須在初始化時分配:

    int x = 5;
    int y = 6;
    int &r = x;
    
  2. 指針在堆棧上有自己的內存地址和大小(x86上為4個字節),而引用共享相同的內存地址(使用原始變量),但也會佔用堆棧上的一些空間。 由於引用與原始變量本身俱有相同的地址,因此可以將引用視為同一變量的另一個名稱。 注意:指針指向的內容可以在堆棧或堆上。 同上一個參考。 我在這個陳述中的主張並不是指針必須指向堆棧。 指針只是一個保存內存地址的變量。 此變量位於堆棧上。 由於引用在堆棧上有自己的空間,並且因為地址與它引用的變量相同。 更多關於堆棧與堆 。 這意味著編譯器不會告訴您存在引用的真實地址。

    int x = 0;
    int &r = x;
    int *p = &x;
    int *p2 = &r;
    assert(p == p2);
    
  3. 您可以指向指向提供額外間接級別的指針的指針。 而引用僅提供一個間接層。

    int x = 0;
    int y = 0;
    int *p = &x;
    int *q = &y;
    int **pp = &p;
    pp = &q;//*pp = q
    **pp = 4;
    assert(y == 4);
    assert(x == 0);
    
  4. 指針可以直接指定為nullptr ,而引用則不能。 如果你足夠努力,並且知道如何,你可以創建一個引用nullptr的地址。 同樣,如果你足夠努力,你可以引用一個指針,然後該引用可以包含nullptr

    int *p = nullptr;
    int &r = nullptr; <--- compiling error
    int &r = *p;  <--- likely no compiling error, especially if the nullptr is hidden behind a function call, yet it refers to a non-existent int at address 0
    
  5. 指針可以遍歷數組,您可以使用++轉到指針指向的下一個項目, + 4轉到第5個元素。 無論指針指向的對像是什麼大小。

  6. 需要使用*取消引用指針以訪問它指向的內存位置,而可以直接使用引用。 指向類/結構的指針使用->來訪問它的成員,而引用使用a .

  7. 指針是保存內存地址的變量。 無論引用如何實現,引用都具有與其引用的項相同的內存地址。

  8. 引用不能填充到數組中,而指針可以是(由用戶@litb提及)

  9. Const引用可以綁定到臨時值。 指針不能(不是沒有一些間接):

    const int &x = int(12); //legal C++
    int *y = &int(12); //illegal to dereference a temporary.
    

    這使得const&更安全的用於參數列表等等。


如果你想變得非常迂腐,你可以用引號做一件事,你不能用指針做:延長臨時對象的生命週期。 在C ++中,如果將const引用綁定到臨時對象,則該對象的生命週期將成為引用的生命週期。

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

在此示例中,s3_copy複製作為串聯結果的臨時對象。 而s3_reference本質上成為臨時對象。 它實際上是對臨時對象的引用,該臨時對象現在具有與引用相同的生命週期。

如果你在沒有const情況下嘗試這個,它將無法編譯。 您不能將非const引用綁定到臨時對象,也不能為此處獲取其地址。


什麼是C ++參考( 適用於C程序員

引用可以被認為是一個常量指針 (不要與指向常量值的指針混淆!)和自動間接,即編譯器將為您應用*運算符。

必須使用非null值初始化所有引用,否則編譯將失敗。 獲取引用的地址既不可能 - 地址運算符將返回引用值的地址 - 也不可能在引用上進行算術運算。

C程序員可能不喜歡C ++引用,因為當間接發生時,或者如果參數通過值或指針傳遞而不查看函數簽名,它將不再是顯而易見的。

C ++程序員可能不喜歡使用指針,因為它們被認為是不安全的 - 雖然引用並不比常量指針更安全,除了在最微不足道的情況下 - 缺乏自動間接的便利性並帶有不同的語義內涵。

請考慮C ++ FAQ中的以下語句:

儘管引用通常是使用底層彙編語言中的地址實現的,但請不要將引用視為指向對象的有趣外觀指針。 參考對象。 它不是指向對象的指針,也不是對象的副本。 這對象。

但如果參考確實是對象,那麼怎麼會有懸空引用呢? 在非託管語言中,引用不可能比指針更“安全” - 通常只是不能跨範圍邊界可靠地對值進行別名!

為什麼我認為C ++引用很有用

來自C背景,C ++引用可能看起來像一個有點愚蠢的概念,但是在可能的情況下仍然應該使用它們而不是指針:自動間接方便的,並且在處理RAII時引用變得特別有用 - 但不是因為任何感知的安全性優點,而是因為它們使寫作慣用代碼不那麼尷尬。

RAII是C ++的核心概念之一,但它與復制語義非常簡單地交互。 通過引用傳遞對象避免了這些問題,因為不涉及復制。 如果語言中沒有引用,則必須使用指針,這些指針使用起來比較麻煩,因此違反了語言設計原則,即最佳實踐解決方案應該比替代方案更容易。


指針和引用之間的區別

指針可以初始化為0而引用不能。實際上,引用也必須引用一個對象,但指針可以是空指針:

int* p = 0;

但我們不能擁有int& p = 0;int& p=5 ;

事實上,要正確地執行它,我們必須首先聲明和定義一個對象,然後我們可以對該對象進行引用,因此前面代碼的正確實現將是:

Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;

另一個重要的一點是,我們可以在沒有初始化的情況下進行指針的聲明,但是在引用的情況下不能做這樣的事情,它必須總是引用變量或對象。然而,這樣使用指針是有風險的,所以通常我們檢查指針是否實際指向某事物。在引用的情況下,不需要這樣的檢查,因為我們已經知道在聲明期間引用對像是必需的。

另一個區別是指針可以指向另一個對象但是引用總是引用同一個對象,讓我們舉個例子:

Int a = 6, b = 5;
Int& rf = a;

Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a.

rf = b;
cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased

另一點:當我們有一個類似STL模板的模板時,這種類模板將始終返回引用而不是指針,以便使用operator []輕鬆讀取或分配新值:

Std ::vector<int>v(10); // Initialize a vector with 10 elements
V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "="

我覺得還有另外一點,這裡沒有涉及。

與指針不同,引用在語法上等同於它們引用的對象,即可以應用於對象的任何操作都用於引用,並且具有完全相同的語法(例外當然是初始化)。

雖然這可能看起來很膚淺,但我相信這個屬性對於許多C ++特性至關重要,例如:

  • 模板。由於模板參數是鴨類型,一種類型的語法屬性是最重要的事情,所以往往是相同的模板可以既使用TT&
    (或者std::reference_wrapper<T>仍然依賴於隱式轉換T&
    模板涵蓋了兩者T&T&&甚至更常見。

  • 左值。考慮語句str[0] = 'X';沒有引用它只適用於c-strings(char* str)。通過引用返回字符允許用戶定義的類具有相同的表示法。

  • 複製構造函數。從語法上講,將對像傳遞給複製構造函數是有意義的,而不是指向對象的指針。但是,複製構造函數無法通過值獲取對象 - 這將導致對同一複制構造函數的遞歸調用。這使引用成為唯一的選擇。

  • 運算符重載。通過引用,可以將間接引入操作員調用 - 例如,operator+(const T& a, const T& b)同時保留相同的中綴符號。這也適用於常規的重載功能。

這些要點賦予了C ++和標準庫的相當大的一部分,因此這是引用的一個主要屬性。


指針和引用之間存在一個根本區別,我沒有看到任何人提到過:引用在函數參數中啟用了引用語法。指針雖然起初不可見,但它們不會:它們只提供按值傳遞的語義。這篇文章已經很好地描述了。

問候,&rzej


該計劃可能有助於理解問題的答案。這是引用“j”和指向變量“x”的指針“ptr”的簡單程序。

#include<iostream>

using namespace std;

int main()
{
int *ptr=0, x=9; // pointer and variable declaration
ptr=&x; // pointer to variable "x"
int & j=x; // reference declaration; reference to variable "x"

cout << "x=" << x << endl;

cout << "&x=" << &x << endl;

cout << "j=" << j << endl;

cout << "&j=" << &j << endl;

cout << "*ptr=" << *ptr << endl;

cout << "ptr=" << ptr << endl;

cout << "&ptr=" << &ptr << endl;
    getch();
}

運行程序,看看輸出,你會明白。

此外,請花10分鐘觀看此視頻:https://www.youtube.com/watch?v=rlJrrGV0iOghttps://www.youtube.com/watch?v=rlJrrGV0iOg


引用和指針都可用於更改另一個函數內的一個函數的局部變量。當它們作為函數的參數傳遞或從函數返回時,它們都可用於保存大對象的複制,以獲得效率增益。儘管有上述相似之處,但引用和指針之間存在以下差異。

引用不如指針強大

1)創建引用後,以後不能引用另一個引用; 它不能重新安置。這通常用指針完成。

2)引用不能為NULL。指針通常為NULL,表示它們沒有指向任何有效的東西。

3)必須在聲明時初始化引用。指針沒有這樣的限制

由於上述限制,C ++中的引用不能用於實現鏈接列表,樹等數據結構。在Java中,引用沒有上述限制,可用於實現所有數據結構。引用在Java中更強大,是Java不需要指針的主要原因。

參考文獻更安全,更易於使用:

1)更安全:由於必須初始化引用,因此不太可能存在像野生指針這樣的野蠻引用。仍然可以使引用不引用有效位置

2)易於使用:引用不需要解除引用操作符來訪問該值。它們可以像普通變量一樣使用。只有在申報時才需要'&'運營商。此外,可以使用點運算符('。')訪問對象引用的成員,而不像需要箭頭運算符( - >)來訪問成員的指針。

與上述原因一起,很少有地方像複製構造函數參數,其中指針不能使用。必須使用引用傳遞複製構造函數中的參數。類似的引用必須用於重載某些運算符,如++


引用永遠不能為NULL


實際上,引用並不像指針。

編譯器保持對變量的“引用”,將名稱與內存地址相關聯; 這是在編譯時將任何變量名轉換為內存地址的工作。

創建引用時,只告訴編譯器為指針變量指定另一個名稱; 這就是為什麼引用不能“指向null”,因為變量不能,也不能。

指針是變量; 它們包含其他變量的地址,或者可以為null。 重要的是指針有一個值,而引用只有一個它正在引用的變量。

現在對實際代碼的一些解釋:

int a = 0;
int& b = a;

在這裡,您不是要創建另一個指向a變量; 你只是在保存a值的內存內容中添加另一個名稱。 此內存現在有兩個名稱, ab ,可以使用任一名稱進行尋址。

void increment(int& n)
{
    n = n + 1;
}

int a;
increment(a);

調用函數時,編譯器通常會為要復制的參數生成內存空間。 函數簽名定義了應該創建的空格,並給出了應該用於這些空間的名稱。 將參數聲明為引用只是告訴編譯器使用輸入變量內存空間而不是在方法調用期間分配新的內存空間。 說你的函數將直接操作調用範圍中聲明的變量似乎很奇怪,但請記住,在執行編譯代碼時,沒有更多的範圍; 只有普通的平坦內存,你的功能代碼可以操縱任何變量。

現在可能存在編譯器在編譯時可能無法知道引用的情況,例如使用extern變量時。 因此,引用可能會也可能不會被實現為底層代碼中的指針。 但是在我給你的例子中,它很可能不會用指針實現。


另一個區別是你可以有一個指向void類型的指針(它意味著指向任何東西的指針)但禁止引用void。

int a;
void * p = &a; // ok
void & p = a;  //  forbidden

我不能說我對這種特殊的差異感到非常滿意。我更傾向於允許帶有地址的含義引用,以及引用的相同行為。它允許使用引用定義一些C庫函數的等價物,如memcpy。


此外,作為內聯函數的參數的引用可以與指針不同地處理。

void increment(int *ptrint) { (*ptrint)++; }
void increment(int &refint) { refint++; }
void incptrtest()
{
    int testptr=0;
    increment(&testptr);
}
void increftest()
{
    int testref=0;
    increment(testref);
}

在內聯指針版本1時,許多編譯器實際上會強制寫入內存(我們正在明確地獲取地址)。但是,他們會將參考文獻保留在更優化的寄存器中。

當然,對於沒有內聯的函數,指針和引用生成相同的代碼,如果它們未被函數修改並返回,則通過值傳遞內在函數而不是引用更好。


除了語法糖之外,引用是一個const指針( 不是指向const指針)。 您必須在聲明引用變量時確定它所引用的內容,並且以後不能更改它。

更新:現在我再考慮一下,有一個重要的區別。

const指針的目標可以通過獲取其地址並使用const轉換來替換。

參考目標不能以任何方式替換UB。

這應該允許編譯器對引用進行更多優化。


與流行的觀點相反,可能有一個NULL引用。

int * p = NULL;
int & r = *p;
r = 1;  // crash! (if you're lucky)

當然,使用參考文件要困難得多 - 但是如果你管理它,你會撕掉你的頭髮試圖找到它。 在C ++中,引用本身並不安全!

從技術上講,這是一個無效的引用 ,而不是空引用。 C ++不支持空引用作為您可能在其他語言中找到的概念。 還有其他類型的無效引用。 任何無效引用都會引發未定義行為的幽靈,就像使用無效指針一樣。

在分配給引用之前,實際錯誤是在NULL指針的解引用中。 但是我不知道任何編譯器會在這種情況下產生任何錯誤 - 錯誤會傳播到代碼中的某個點。 這就是讓這個問題如此陰險的原因。 大多數情況下,如果你取消引用一個NULL指針,你就會在那個位置崩潰,並且不需要太多的調試就可以搞清楚。

我上面的例子簡短而且做作。 這是一個更真實的例子。

class MyClass
{
    ...
    virtual void DoSomething(int,int,int,int,int);
};

void Foo(const MyClass & bar)
{
    ...
    bar.DoSomething(i1,i2,i3,i4,i5);  // crash occurs here due to memory access violation - obvious why?
}

MyClass * GetInstance()
{
    if (somecondition)
        return NULL;
    ...
}

MyClass * p = GetInstance();
Foo(*p);

我想重申,獲取空引用的唯一方法是通過格式錯誤的代碼,一旦你擁有它,你就會得到未定義的行為。 檢查空引用是沒有意義的; 例如,您可以嘗試if(&bar==NULL)...但編譯器可能會優化語句不存在! 有效的引用永遠不能為NULL,因此從編譯器的視圖來看,比較總是為false,並且可以將if子句作為死代碼消除 - 這是未定義行為的本質。

避免麻煩的正確方法是避免取消引用NULL指針來創建引用。 這是實現這一目標的自動化方法。

template<typename T>
T& deref(T* p)
{
    if (p == NULL)
        throw std::invalid_argument(std::string("NULL reference"));
    return *p;
}

MyClass * p = GetInstance();
Foo(deref(p));

對於那些具有更好寫作技巧的人來看這個問題,請參閱Jim Hyslop和Herb Sutter的Null References

有關解除引用空指針的危險的另一個示例,請參閱Raymond Chen 嘗試將代碼移植到另一個平台時暴露未定義的行為


你忘記了最重要的部分:

使用指針的成員訪問->
使用引用的成員訪問.

foo.bar 明顯優於foo->bar ,就像vi 明顯優於Emacs :-)


我總是決定this從C ++核心指導原則:

當“無參數”是有效選項時,首選T *而不是T.


引用不是給予某些內存的另一個名稱。它是一個不可變的指針,在使用時會自動取消引用。基本上它歸結為:

int& j = i;

它在內部成為

int* const j = &i;

引用與指針非常相似,但它們經過精心設計,有助於優化編譯器。

  • 設計引用使得編譯器更容易跟踪哪個引用別名哪個變量。 兩個主要特徵非常重要:沒有“參考算術”,也沒有重新分配參考文獻。 這些允許編譯器在編譯時找出哪些引用別名是哪些變量。
  • 允許引用引用沒有內存地址的變量,例如編譯器選擇放入寄存器的變量。 如果你取一個局部變量的地址,編譯器很難把它放在一個寄存器中。

舉個例子:

void maybeModify(int& x); // may modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // This function is designed to do something particularly troublesome
    // for optimizers. It will constantly call maybeModify on array[0] while
    // adding array[1] to array[2]..array[size-1]. There's no real reason to
    // do this, other than to demonstrate the power of references.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(array[0]);
        array[i] += array[1];
    }
}

優化編譯器可能會意識到我們正在訪問[0]和[1]相當多的一組。 它希望優化算法:

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Do the same thing as above, but instead of accessing array[1]
    // all the time, access it once and store the result in a register,
    // which is much faster to do arithmetic with.
    register int a0 = a[0];
    register int a1 = a[1]; // access a[1] once
    for (int i = 2; i < (int)size; i++) {
        maybeModify(a0); // Give maybeModify a reference to a register
        array[i] += a1;  // Use the saved register value over and over
    }
    a[0] = a0; // Store the modified a[0] back into the array
}

為了進行這樣的優化,需要證明在調用期間沒有任何東西可以改變數組[1]。 這很容易做到。 我永遠不會少於2,所以array [i]永遠不會引用數組[1]。 maybeModify()被賦予a0作為參考(別名數組[0])。 因為沒有“引用”算法,編譯器只需要證明maybeModify永遠不會得到x的地址,並且它已經證明沒有任何改變數組[1]。

它還必須證明,當我們在a0中有一個臨時寄存器副本時,未來的調用沒有辦法讀/寫[0]。 這通常是微不足道的,因為在很多情況下很明顯,引用永遠不會存儲在像類實例這樣的永久結構中。

現在用指針做同樣的事情

void maybeModify(int* x); // May modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Same operation, only now with pointers, making the
    // optimization trickier.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(&(array[0]));
        array[i] += array[1];
    }
}

行為是一樣的; 只是現在更難以證明maybeModify不會修改數組[1],因為我們已經給它一個指針; 這隻貓已經不在了。 現在它必須做更加困難的證明:對maybeModify進行靜態分析以證明它永遠不會寫入&x + 1.它還必須證明它永遠不會保存可以引用數組[0]的指針,這只是太棘手了。

現代編譯器在靜態分析方面越來越好,但總是很好地幫助它們並使用引用。

當然,除非進行這種巧妙的優化,否則編譯器確實會在需要時將引用轉換為指針。

編輯:發布這個答案五年後,我發現了一個實際的技術差異,其中引用不同於查看相同尋址概念的不同方式。 引用可以以指針不能的方式修改臨時對象的生命週期。

F createF(int argument);

void extending()
{
    const F& ref = createF(5);
    std::cout << ref.getArgument() << std::endl;
};

通常,臨時對象(例如通過調用createF(5)創建的對象createF(5)在表達式的末尾被銷毀。 但是,通過將該對象綁定到引用, ref ,C ++將延長該臨時對象的生命週期,直到ref超出範圍。


指針和引用之間存在非常重要的非技術差異:通過指針傳遞給函數的參數比通過非const引用傳遞給函數的參數更加明顯。 例如:

void fn1(std::string s);
void fn2(const std::string& s);
void fn3(std::string& s);
void fn4(std::string* s);

void bar() {
    std::string x;
    fn1(x);  // Cannot modify x
    fn2(x);  // Cannot modify x (without const_cast)
    fn3(x);  // CAN modify x!
    fn4(&x); // Can modify x (but is obvious about it)
}

回到C,看起來像fn(x)只能通過值傳遞的調用,所以它肯定無法修改x;修改您需要傳遞指針的參數fn(&x)。因此,如果參數之前沒有參數,則&您知道它不會被修改。 (相反,&意味著修改,不是真的,因為你有時必須通過const指針傳遞大的只讀結構。)

有些人認為,在閱讀代碼時,這是一個非常有用的功能,指針參數應始終用於可修改的參數而不是非const引用,即使函數從不期望a nullptr。也就是說,那些人認為fn3()不應該允許像上面這樣的功能簽名。Google的C ++風格指南就是一個例子。


冒著混淆的風險,我想拋出一些輸入,我確定它主要取決於編譯器如何實現引用,但是在gcc的情況下,引用只能指向堆棧上的變量實際上並不正確,以此為例:

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

哪個輸出:

THIS IS A STRING
0xbb2070 : 0xbb2070

如果您注意到內存地址完全相同,則意味著引用成功指向堆上的變量!現在,如果你真的想變得怪異,這也有效:

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

哪個輸出:

THIS IS A STRING

因此引用是引擎蓋下的一個指針,它們都只是存儲一個內存地址,地址所指向的是無關緊要的,如果我調用std :: cout << str_ref;你會怎麼想?在調用delete&str_ref之後?好吧,顯然它編譯得很好,但是在運行時導致分段錯誤,因為它不再指向有效變量,我們基本上有一個仍然存在的破壞引用(直到它超出範圍),但是沒用。

換句話說,引用只不過是一個指針,它將指針機制抽像出來,使其更安全,更容易使用(沒有偶然的指針數學,沒有混淆'。'和' - >'等),假設你不要像我上面的例子那樣嘗試任何廢話;)

現在無論編譯器如何處理引用,它總是會有一些指針,因為引用必須引用特定內存地址的特定變量才能使它按預期工作,沒有解決這個問題(因此術語“參考”)。

唯一要記住引用的主要規則是它們必須在聲明時定義(除了頭中的引用外,在這種情況下它必須在構造函數中定義,在它包含的對象之後是構造它來定義它為時已晚)。

請記住,我上面的例子就是這樣,展示參考資料的例子,你永遠不會想要以這些方式使用參考!為了正確使用參考文獻,這裡已經有很多答案可以解決問題


引用是另一個變量的別名,而指針保存變量的內存地址。引用通常用作函數參數,以便傳遞的對像不是副本而是對象本身。

    void fun(int &a, int &b); // A common usage of references.
    int a = 0;
    int &b = a; // b is an alias for a. Not so common to use. 

在C ++中可以引用指針,但反過來不可能意味著指向引用的指針是不可能的。對指針的引用提供了更清晰的語法來修改指針。看看這個例子:

#include<iostream>
using namespace std;

void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap(str1, str2);
  cout<<"str1 is "<<str1<<endl;
  cout<<"str2 is "<<str2<<endl;
  return 0;
}

並考慮上述程序的C版本。在C中你必須使用指向指針(多個間接),它會導致混亂,程序可能看起來很複雜。

#include<stdio.h>
/* Swaps strings by swapping pointers */
void swap1(char **str1_ptr, char **str2_ptr)
{
  char *temp = *str1_ptr;
  *str1_ptr = *str2_ptr;
  *str2_ptr = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap1(&str1, &str2);
  printf("str1 is %s, str2 is %s", str1, str2);
  return 0;
}

有關指針引用的更多信息,請訪問以下內容:

正如我所說,指向引用的指針是不可能的。嘗試以下程序:

#include <iostream>
using namespace std;

int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}






c++-faq