Pointer arithmetic and integral promotion

1.7k views Asked by At

In the expression p + a where p is a pointer type and a is an integer, will integer promotion rules apply? For example, if a is a char, on a 64-bit machine it will surely be extended to 64 bit before being added to the pointer value (in the compiled assembly), but is it specified by the standards? What will it be promoted to? int, intptr_t or ptrdiff_t? What will unsigned char or size_t be converted to?

4

There are 4 answers

0
alk On

I'd say normal integer promotion is applied to a. The C-Standard does not provide any specific rules for the conversion of the integer part of an arithmetic operation on a pointer.

That is, as a is declared char, it is converted to an int prior to being passed to the + operator.

If one adds a size_t it either stays what size_t is defined to be or if (for whatever reasons) it has a smaller rank then int it is promoted to an int.

0
Vlad from Moscow On

Yes, it is specified in the C++ Standard (paragraph #1 section 5.7 Additive operators) that

the usual arithmetic conversions are performed for operands of arithmetic or enumeration type.

For types (for example char or unsigned char) that have rank less than int the integral promotions will be performed. For size_t (size_t has a rank that is not less than the rank of int or unsigned int) nothing will be done because there is no a second operand of arithmetic type.

0
Glenn Teitelbaum On

It does not seem required by the standard for any promotion to occur since char is an integral type:

For addition, either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral or unscoped enumeration type

It seems implementations may depend on the type of pointer additions allowed by the underlying architecture - so if the archtecture supports address+BYTE - all is good with char - if not it will likely promote to the smallest address offset size supported.

The result of subtraction of pointers is defined to be of type `std::ptrdiff_t'

When two pointers to elements of the same array object are subtracted, the result is the difference of the subscripts of the two array elements. The type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined as std::ptrdiff_t in the header

0
Cheers and hth. - Alf On

C++11 §5.7/1:

“The additive operators + and - group left-to-right. The usual arithmetic conversions are performed for operands of arithmetic or enumeration type.”

This apparently reduces the problem to considering the usual arithmetic conversions, defined by …

C++11 §5/9:

“Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result This pattern is called the usual arithmetic conversions, which are defined as follows:

  • If either operand is of scoped enumeration type (7.2), no conversions are performed; if the otheroperand does not have the same type, the expression is ill-formed.

  • If either operand is of type long double, the other shall be converted to long double.

  • Otherwise, if either operand is double, the other shall be converted to double.

  • Otherwise, if either operand is float, the other shall be converted to float.

  • Otherwise, the integral promotions (4.5) shall be performed on both operands. Then the following rules shall be applied to the promoted operands:

    • If both operands have the same type, no further conversion is needed.

    • Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank shall be converted to the type of the operand with greater rank.

    • Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.

    • Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type shall be converted to the type of the operand with signed integer type.

    • Otherwise, both operands shall be converted to the unsigned integer type corresponding to the type of the operand with signed integer type.”

Followed mechanically, this set of rules would end up in the last bullet point (dash in the standard) and convert a pointer operand to the unsigned integer-type corresponding to something non-existing. Which is just wrong. So the wording “The usual arithmetic conversions are performed for operands of arithmetic or enumeration type” can not be interpreted literally – it's IMHO defective – but must be interpreted like “The usual arithmetic conversions are performed for invocations where both operands are of arithmetic or enumeration type“

So, promotions as such, which are invoked via the usual arithmetic conversions, don't come into play when one operand is a pointer.

But a bit further down in §5.7 one finds …

C++11 §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.”

This defines the result entirely in terms of array indexing. For a char array the difference of subscripts can exceed the range of ptrdiff_t. A reasonable way for an implementation to arrange this, is to convert the non-pointer argument to the unsigned integral type size_t (effectively sign extension at the bit level), and use that value with modular arithmetic to compute the resulting pointer value.