[英]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.