簡體   English   中英

指針算術:沒有解除引用的超出范圍

[英]Pointer arithmetic: out of bound without dereferencing

我想知道C ++標准是否接受以下代碼。

int n{ 10 };
double* p = new double[0];
double* q = p + n;
std::cout << "n = " << static_cast<int>(q - p) << std::endl;

我希望該程序顯示n的值。

由於這個問題可能看起來很奇怪,這里是對這個問題起源的解釋。 我想在2D中設計一個動態數組類(想想一個容器的std :: vector類,但在2D而不是1D)。 一個簡單的方法是:

template <typename T>
class Array2D<T> {
private:
    T* data_;
    int nb_rows_;
    int nb_columns_;
public:
    ...
};

不幸的是,這種設計並不像SIMD那樣友好

Array2D<int> A(5, 6);
for (int i = 0; i < A.nb_rows(); ++i) {
    for (int j = 0; j < A.nb_columns(); ++j) {
        A(i, j) += 1;
    }
}

因為指針別名,編譯器無法確定循環期間nb_columns_是否未更改,因此無法進行向量化。 因此,我使用與std :: vector的大多數實現相同的設計,其中向量的大小在指針中“隱藏”。

template <typename T>
class Array2D<T> {
private:
    T* data_;
    T* nb_rows_;
    T* nb_columns_;
public:
    Array2D(int n, int p) {
        data_ = new T[n * p];
        nb_rows_ = data_ + n;
        nb_columns_ = data_ + p;
    }
    ...
    int nb_columns() const {
        return static_cast<int>(nb_columns_ - data_);
    }
    ...
};

只要n> = 1和p> = 1,該設計就可以很好地工作。但是,如果n = 0且p = 5,則最終會遇到上述“問題”。 由於類中的以下方法,構造具有0行的Array2D可能會很有用

void push_back(const Array1D<T>& B);

采用大小為p的Array1D(用斷言檢查)並向我的Array2D對象添加一行。 你可以這樣做:

Array2D<double> A(0, 10);
Array1D<double> B(10);

// work with B
A.push_back(B);

該代碼在clang,g ++和icpc上運行良好,但我仍然想知道它是否有效。 C ++ 11標准的5.7節是關於該問題的,但是談論“數組對象”。 我想知道我的p是否指向所謂的“數組對象”,或者數組對象是否是諸如“ double p [5]”之類的東西。

這是未定義的行為。 在實踐中,它可能適用於大多數現代系統,但過去曾存在導致程序崩潰的系統。 指針是只是一種特殊類型的整數; 它可以具有各種結構,只需將指向未映射的內存的指針加載到寄存器中就可能導致陷阱。

從標准(強調添加),§5.7/ 5:

當向指針添加或從指針中減去具有整數類型的表達式時,結果具有指針操作數的類型。 如果指針操作數指向數組對象的元素, 並且數組足夠大 ,則結果指向與原始元素偏移的元素,以使結果數組元素和原始數組元素的下標之差等於整數表達式。 換句話說,如果表達式P指向數組對象的第i個元素,則表達式(P)+ N(相當於N +(P))和(P)-N(其中N的值為n)表示分別為數組對象的第i + n和第i - 第n個元素,只要它們存在。 此外,如果表達式P指向數組對象的最后一個元素,則表達式(P)+1指向數組對象的最后一個元素之后,如果表達式Q指向數組對象的最后一個元素之后,表達式(Q)-1指向數組對象的最后一個元素。 如果指針操作數和結果都指向同一數組對象的元素,或者指向數組對象的最后一個元素,則求值不會產生溢出; 否則,行為未定義。

最后一句話很重要:“否則,行為是不確定的”。

double* q = p + n;

該行導致未定義的行為。 [expr.add] / 5指定

當向指針添加或從指針中減去具有整數類型的表達式時,結果具有指針操作數的類型。 […]如果指針操作數和結果都指向同一數組對象的元素,或者指向數組對象的最后一個元素之后,則評估不應產生溢出; 否則,行為未定義。

結果不指向p的元素,因此最后一句適用。 它肯定可以在您對其進行測試的任何系統上運行*,但是該標准並未以任何方式覆蓋它。


不幸的是,該標准似乎沒有定義“數組對象”是什么。

數組對象只是一個具有數組類型的對象,簡而言之:一個數組。

int a[5];

聲明一個數組(對象),其中包含五個元素,size sizeof int * 5 還考慮[expr.add] / 4:

出於這些運算符的目的,指向非數組對象的指針的行為與指向長度為1的數組的第一個元素的指針相同,該對象的類型為其元素類型。

* 您使用的系統可能會將指針中的地址表示為簡單整數,並且加法和減法只會調用簡單的算術運算。 可能還有系統檢查加載到寄存器中的指針值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM