指针算术:超出范围而无需取消引用 [英] Pointer arithmetic: out of bound without dereferencing

查看:157
本文介绍了指针算术:超出范围而无需取消引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道C ++标准是否不接受以下代码.

I would like to know if the following code is not accepted by the C++ standard.

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

我希望该程序显示n的值.

I want that program to display the value of n.

由于这个问题可能看起来很怪异,所以在这里解释了此问题的由来.我想在2D中设计一个动态数组类(想像一种std :: vector容器,但以2D而不是1D的形式).一种简单的方法是:

As this question might look weird, here is the explanation of the origin of this problem. I want to design a dynamic array class in 2D (think of a std::vector kind of a container but in 2D instead of 1D). A straightforward approach would be:

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

不幸的是,这种设计不是SIMD友好的循环,例如

Unfortunately, this design is not SIMD friendly as a loop such as

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实现相同的设计,其中向量的大小在指针中隐藏".

would fail to vectorize as the compiler can't be sure if nb_columns_ is not changed during the loop because of pointer aliasing. Therefore, I use the same design as most implementation of std::vector where the size of the vector is "hidden" in a pointer.

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可能会很有用,因为我的课程中采用了以下方法

This design works well as long as n >= 1 and p >= 1. But if n = 0 and p = 5, you end up with the kind of "problem" explained above. Constructing an Array2D with 0 rows might be useful because of the following method in my class

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

的大小为Array1D(使用断言检查),并向我的Array2D对象添加一行.您可以这样做:

that takes an Array1D of size p (it is checked with an assert) and adds a row to my Array2D object. You could do:

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]"之类的东西.

The code works fine on clang, g++ and icpc but I am still wondering if it is valid. Section 5.7 of the C++11 standard is about that problem but talk about "array objects". I am wondering if my p points to what they call an "array object", or if an array object is something such as "double p[5]".

推荐答案

这是未定义的行为.实际上,它可能会在大多数现代系统上运行,但是过去有一些系统会导致程序崩溃.指针不是唯一的特殊类型的整数.它可以具有各种结构,仅将指向未映射内存的指针加载到寄存器中可能会导致陷阱.

It's undefined behavior. In practice, it will probably work on most modern systems, but there have been systems in the past where it would cause the program to crash. A pointer is not just a special type of integer; it can have all sorts of structure, and just loading a pointer to unmapped memory into a register can cause a trap.

从标准(加强调)起,第5.7/5节:

From the standard (emphasis added), §5.7/5:

将具有整数类型的表达式添加到或 从指针中减去,结果的类型为 指针操作数.如果指针操作数指向的元素 一个数组对象,并且数组足够大,结果 指向与原始元素偏移的元素,使得 结果和原始下标的差异 数组元素等于整数表达式.换一种说法, 如果表达式P指向数组的第i个元素 对象,则表达式(P)+ N(相当于N +(P))和(P)-N (其中N的值为n)分别指向第i + n个 和i-数组对象的第n个元素(如果存在). 此外,如果表达式P指向 数组对象,表达式(P)+1指向最后一个 数组对象的元素,如果表达式Q指向一个 在数组对象的最后一个元素之后,表达式(Q)-1 指向数组对象的最后一个元素. 如果 指针操作数,结果指向相同的元素 数组对象或数组对象的最后一个元素之后, 评估不得产生溢出;否则, 行为是不确定的.

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.

最后一句话很重要:否则, 行为是不确定的".

That last sentence is the important one: "otherwise, the behavior is undefined".

这篇关于指针算术:超出范围而无需取消引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆