AUTOSAR 规则 A5-0-4 规定
指针算术不得与指向非最终的指针一起使用 课程。
它提供了以下理由:
只有当指针的指向类型是正确的时,指针算术才被明确定义 指针等于它所指向的数组的元素类型,否则 该行为是未定义的。只有在以下情况下才能保证此属性: 指针操作数是指向非类类型的指针或指向最终类型的指针 班级类型。
然后给出了一些合规和不合规的例子。其中一项不合规之处让我感到困惑:
void Foo(Base *start, size_t len)
{
// Non-Compliant: pointer arithmetic on non-final pointer type
for (Base *iter = start; iter != start + len; ++iter)
{
iter->Do();
}
}
无论任何人不太可能使用普通的 C 数组来存储多态指针,它都存在于 C++ 语言的结构中。
所以我的直觉是上面的代码没有任何问题。
也许我的这个信念是错误的。 或者也许我完全忽略了这个 AUTOSAR 规则试图向读者传达的观点。
有人能比 AUTOSAR 文档更好地解释它吗?
另请参阅 AUTOSAR 规则 A5-0-4,其中给出了示例中使用的完整代码。
我怀疑你没有抓住要点。考虑这样的事情:
class Base {
int x;
public:
virtual void Do() = 0;
virtual ~Base() = default;
};
class Derived : public Base {
int y;
public:
virtual void Do() override {
++y;
}
};
int main() {
Derived data[10];
Foo(data, 10); // using definition of `Foo` from question
}
这具有未定义的行为。
Foo
期望接收 Base
数组的开头地址。但我们传递的是指向 Derived
的指针,而不是指向 Base
的指针。由于 Foo
“认为”它正在操作指向基址的指针,因此当其循环执行 ++iter
时,它会将地址增加 sizeof(Base)
。
然而,正如我们所定义的,
Derived
几乎肯定比Base
大。在增量 iter
几乎可以肯定 won't 指向第二个 Derived
对象时,当我们尝试在该指针上调用 Do
时,事情会向侧面发展(不能保证,但在典型的实现中,它最终会尝试使用 data[0].y
作为 vtable 指针来查找 Do
的地址。
如果是的话,所质疑的例子就很好了
void Foo(Base **start, size_t len);
即 start 类型为
Base **
,但每个增量都是 sizeof(Base)
对象而不是 sizeof(Base*)
。