2010/03/26

[C++] 使用Pointer to pointer或Reference to pointer,改變pointer的address

在C語言裡,我們可以使用pointer指向一個實體,因此如果把實體變數的address傳到一個funtion的pointer,那就可以改變此pointer所指向的實體的值。

但是如果我們要改變的不是pointer所指向的實體的值,而是pointer本身的值,那就把pointer當作一種type,再把它的address傳到funtion的double pointer。以下將舉一個例子說明。

假設我們有一個function readData可以從網路抓取packet後,填到一個unsigned型態的的buffer(以pointer傳入),接著我們會使用一個function parse1來分析前半段的buffer,然後在使用function parse2來分析後半段的buffer。在parse的程式碼中,我們使用(buffer++) 來移動buffer的pointer,以利往下讀取資料。

在下面的程式碼中,我們在parse1和parse2都是使用unsigned *buffer來接住main裡的buffer的位址(假設在0x100),因此parse1和parse2接收到的pointer的值都是副本,也就是0x100。所以即使在parse1移動buffer pointer,也不會影響在main的buffer的位址。所以,當parse2呼叫時,傳入buffer pointer的值,依然是0x100,還是從buffer的頭開始分析。

image

一個錯誤的例子

#include <iostream>
using namespace std;
#define BUFFER_SIZE 10

void readData(unsigned *buffer, int size) // Ex:從網路讀取資料寫道buffer
{
    for (int i = 0; i < size ; i++)    {
        buffer[i] = i;
    }   
}

void parse1(unsigned *buffer, int size) // 開始第一階段parse讀取到的資訊
{
    for (int i = 0; i < size ; i++)    {
        cout << "processing with parser1:" << *(buffer++) << endl; // 每讀取一個元素後,指標移動
    }   
}

void parse2(unsigned *buffer, int size) // 希望從第一階段的parse後,繼續第二階段的parse
{
    for (int i = 0; i < size ; i++)    {
        cout << "processing with parser2:" << *(buffer++) << endl;
    }   
}

int main(void)
{
    unsigned buffer[BUFFER_SIZE];
    readData(buffer, BUFFER_SIZE);
    parse1(buffer,BUFFER_SIZE/2);
    parse2(buffer,BUFFER_SIZE/2); // 此時buffer的pointer,並不會從一半開始,還是原本的頭開始
    return 0;
    /* output:
    processing with parser1:0
    processing with parser1:1
    processing with parser1:2
    processing with parser1:3
    processing with parser1:4
    processing with parser2:0
    processing with parser2:1
    processing with parser2:2
    processing with parser2:3
    processing with parser2:4

    */
  
}

 

我們可以使用pointer to pointer達到修改pointer的值的目的。方法如下:先使用 unsigned *p = buffer; 來複製一份buffer的address。因此p的值為0x100,並且假設p的address為0x500。然後在parse裡使用unsigned **buffer_p指向p,因使buffer_p的值為0x500。接著我們可以使用(*buffer_p)++來改變p的值,也就是p所指向的位置可以跟個改變。所以在parse1執行完後,p的值,就會改變成0x200,所以再把p的address給parse1後,buffer_p的值雖然一樣式0x500,但是buffer_p所指向的值就變成0x200。因此使用*((*buffer_p)++) 可以指向p,再指向buffer裡實體的unsigned變數

Note,由於array的address不可以改,所以我們才需要一個p來記錄buffer的address,並且透過修改p的值來達到移動pointer的目的。
  image

一個正確的例子(pointer to pointer)

#include <iostream>
using namespace std;
#define BUFFER_SIZE 10

void readData(unsigned *buffer, int size)
{
    for (int i = 0; i < size ; i++)    {
        buffer[i] = i;
    }   
}

void parse1(unsigned **buffer_p, int size)
{
    for (int i = 0; i < size ; i++)    {
        cout << "processing with parser1:" << *((*buffer_p)++) << endl;;
    }   
}

void parse2(unsigned **buffer_p, int size)
{
    for (int i = 0; i < size ; i++)    {
        cout << "processing with parser2:" << *((*buffer_p)++) << endl;;
    }   
}

int main(void)
{
    unsigned buffer[BUFFER_SIZE];
    unsigned *p = buffer;
    readData(buffer, BUFFER_SIZE);
    parse1(&p,BUFFER_SIZE/2);
    parse2(&p,BUFFER_SIZE/2);
    return 0; 
   /* output: 
    processing with parser1:0
    processing with parser1:1
    processing with parser1:2
    processing with parser1:3
    processing with parser1:4
    processing with parser2:5
    processing with parser2:6
    processing with parser2:7
    processing with parser2:8
    processing with parser2:9
    */

}

如果你覺得看不習慣pointer to pointer,可以使用reference to pointer也可以達到同樣目的。請先閱讀這篇:

[C++] Call by value、Call by pointer、Call by reference

一個正確的例子(reference to pointer)

#include <iostream>
using namespace std;
#define BUFFER_SIZE 10

void readData(unsigned *buffer, int size)
{
    for (int i = 0; i < size ; i++)    {
        buffer[i] = i;
    }   
}

void parse1(unsigned *&buffer, int size)
{
    for (int i = 0; i < size ; i++)    {
        cout << "processing with parser1:" << *(buffer++) << endl;;
    }   
}

void parse2(unsigned *&buffer, int size)
{
    for (int i = 0; i < size ; i++)    {
        cout << "processing with parser2:" << *(buffer++) << endl;;
    }   
}

int main(void)
{
    unsigned buffer[BUFFER_SIZE];
    unsigned *p = buffer;
    readData(buffer, BUFFER_SIZE);
    parse1(p,BUFFER_SIZE/2);
    parse2(p,BUFFER_SIZE/2);
    return 0;
/* output:
    processing with parser1:0
    processing with parser1:1
    processing with parser1:2
    processing with parser1:3
    processing with parser1:4
    processing with parser2:5
    processing with parser2:6
    processing with parser2:7
    processing with parser2:8
    processing with parser2:9
    */
}

沒有留言:

2024年React state management趨勢

輕量化 在過去Redux 是 React 狀態管理的首選函式庫。 Redux 提供了強大的功能和靈活性,但也帶來了一定的學習成本和複雜度。 隨著 React 生態的不斷發展,越來越多的開發者開始追求輕量化的狀態管理函式庫。 Zustand 和 Recoil 等庫以其簡單易用、性...