2014-10-29 40 views
2

我想知道下面的代碼是否被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(檢查與一個assert)並增加了一個行到我Array2D對象的是有用的。你可以這樣做:

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

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

的代碼工作正常上鐺,g ++以及ICPC但我還是想知道,如果它是有效的。 C++ 11標準的第5.7節就是關於這個問題,但討論了「數組對象」。我想知道,如果我的p指向他們所謂的「數組對象」,或者如果一個數組對象是諸如「double p [5]」之類的東西。

+1

@Joachim Pileborg:我想確定這一點。你能引用這個標準嗎?當data_ + n可能溢出時,可能還有一些指針溢出。 – InsideLoop 2014-10-29 09:34:12

+0

我不知道用零大小的數組會做什麼? – 2014-10-29 09:34:13

+0

正如Ali Kazmi所說,零大小的陣列有什麼好處?難道你不能只是檢查是否n * p == 0然後就像這樣返回:'Array2D(int n,int p){if(n * p == 0)return; data_ = new T [n * p]; nb_rows_ = data_ + n; nb_columns_ = data_ + p; }' – 2014-10-29 09:36:54

回答

4

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

從標準(強調),§5.7/ 5:

When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i + n-th and i − n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

這最後一句是最重要的一個: 「否則, 行爲是不確定的」。

+0

這是我所指的標準的一部分。不幸的是,標準似乎並沒有定義「數組對象」是什麼。我想知道它是否僅僅是double p [5]或者double * p = new double [5]之類的東西。你有沒有在標準中找到任何「數組對象」的內容? – InsideLoop 2014-10-29 09:55:43

+1

@InsideLoop如果你分配一個數組(例如'new [...]),那麼結果指針是一個指向數組對象的指針。另外,如果你聲明一個數組,它也是一個數組對象。通過閱讀C++ 11標準的5.3.4美元/ 1來澄清。另見例如$ 18.6.1.2/1。 – 2014-10-29 09:58:55

+1

@InsideLoop:檢查上面的段落:「7爲了這些運算符的目的,指向非數組元素的對象的指針的行爲與指向長度爲1的數組的第一個元素的指針相同對象的類型作爲其元素類型「,所以在這種情況下,一切都是一個數組。 – 2014-10-29 09:59:11

3
double* q = p + n; 

此行引起未定義的行爲。 [expr.add]/5指定

When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. […] If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

結果不指向的p的元件,因此,最後一句適用。它肯定會在你測試它的任何系統上工作*,但它不以任何方式覆蓋標準。


Unfortunately, the standard does not seem to define what an "array object" is.

數組對象只是與數組類型的對象,不久地講:數組。

int a[5]; 

聲明與五行和大小sizeof int * 5的陣列(對象)。還要考慮[expr.add/4:

For the purposes of these operators, a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

* 您使用指針一樣簡單整數,加法和減法只會調用簡單的算術題大概會表示地址的系統。也可能有系統檢查加載到寄存器的指針值。