15

The C++17 standard seems to say that an integer can only be added to a pointer if the pointer is to an array element, or, as a special exception, the pointer is the result of unary operator &:

8.5.6 [expr.add] describing addition to a pointer:

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 expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 ≤ i + j ≤ n; otherwise, the behavior is undefined.

This quote includes a non-normative footnote:

An object that is not an array element is considered to belong to a single-element array for this purpose; see 8.5.2.1

which references 8.5.2.1 [expr.unary.op] discussing the unary & operator:

The result of the unary & operator is a pointer to its operand... For purposes of pointer arithmetic (8.5.6) and comparison (8.5.9, 8.5.10), an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T.

The non-normative footnote seems to be slightly misleading, as the section it references describes behavior specific to the result of unary operator &. Nothing appears to permit other pointers (e.g. from non-array new) to be considered single-element arrays.

This seems to suggest:

void f(int a) {
    int* z = (new int) + 1; // undefined behavior
    int* w = &a + 1; // ok
}

Is this an oversight in the changes made for C++17? Am I missing something? Is there a reason that the "single-element array rule" is only provided specifically for unary operator &?

Note: As specified in the title, this question is specific to C++17. The C standard and prior versions of the C++ standard contained clear normative language that is no longer present. Older, vague questions like this are not relevant.

0x5f3759df
  • 2,349
  • 1
  • 20
  • 25
  • 2
    "*The non-normative footnote seems to be slightly misleading*" That's what non-normative notations are for. They explain in plain English something that is detailed more explicitly elsewhere. "*Is there a reason that the "single-element array rule" is only provided specifically for unary operator &?*" Is there a reason to provide it for something else? – Nicol Bolas Jun 18 '18 at 18:43
  • 8
    @NicolBolas If you allocate a single object using `new`, you still have a pointer to a single object without using `&`, as mentioned by OP. The question is whether this rule also applies in this case (or in related cases). – Holt Jun 18 '18 at 18:52
  • 4
    Looks like an oversight to me, since `p==&*p` by definition. – n. m. could be an AI Jun 18 '18 at 19:12
  • Hm, I personally interpret 8.5.2.1 as: *If* we take an address this way, *then* we have... There is nothing mentioned for the *else* explicitly, so state is yet **open**. 8.5.6 generalises the matter in the foot note, expanding the one-element-array rule to the *else* case. The normative expansion -- see @n.m. 's comment... – Aconcagua Jul 29 '18 at 04:50
  • Related: [issue 1596](http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1596) – xskxzr Aug 30 '18 at 14:56
  • Not to take this example too literally, but if this is a serious concern, you do have the option of `new[]`. – Davislor Jan 03 '19 at 03:06
  • Is this question a result of an independent research or it was inspired by https://groups.google.com/a/isocpp.org/d/msg/std-discussion/D39lt8oEH8Q/0rDxDt47CQAJ ? – Language Lawyer Jan 09 '19 at 13:56
  • @LanguageLawyer This question arose independently while I was reading the standard. If I recall correctly I was originally checking to see if one could legally increment a pointer after the object it referred to had been deallocated when I noticed this array requirement. – 0x5f3759df Jan 17 '19 at 00:54
  • 1
    Fixed in https://github.com/cplusplus/draft/pull/3485/commits/ec295895ee06264dd62922332cfa49f71f0b181a – Language Lawyer Dec 01 '19 at 04:17

1 Answers1

7

Yes, this appears to be a bug in the standard.

int* z = (new int)+1; // undefined behavior.
int* a = new int;
int* b = a+1; // undefined behavior, same reason as `z`
&*a; // seeming noop, but magically makes `*a` into an array of one element!
int* c = a+1; // defined behavior!

this is pretty ridiculous.

8.5.2.1 [expr.unary.op]

[...] an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T

once "blessed" by 8.5.2.1, the object is an array of one element. If you don't bless it by invoking & at least once, it has never been blessed by 8.5.2.1 and is not an array of one element.

It was fixed as a defect in .

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    Every C or C++ Standard has followed C89's tradition of expecting compiler writers to recognize that in cases where some parts of the Standard defines the behavior of some category of actions, some other part says an overlapping general category of actions invokes UB, actions which fall into both categories should be treated in the defined fashion when there is no sensible reason to do otherwise. Compiler writers who recognize that won't need Standards to complicate the specification of the second category by carving out all of the exemptions, and those who don't are unlikely... – supercat Aug 29 '18 at 19:19
  • 1
    ...to produce quality compilers even if the Standards carve out all the needed exemptions. The failure of the Standards to carve out every exemption from rules identifying general categories of UB would not be a defect if they were interpreted as designed. – supercat Aug 29 '18 at 19:22
  • 2
    Fixed in https://github.com/cplusplus/draft/pull/3485/commits/ec295895ee06264dd62922332cfa49f71f0b181a – Language Lawyer Dec 01 '19 at 04:18