プログラミング言語C/C++における、配列型とポインタ演算の知られざる*1落とし穴。
問題:下記sum_odd
関数の実行結果は?
int data[5] = { 1, 2, 3, 4, 5 }; // 奇数番目の要素値のみを合計する int sum_odd() { int s = 0; int *endp = data + 5; // 配列末尾要素の次位置 // ポインタ値p が endp を越えるまでループ処理 for (int *p = data; p < endp; p += 2) { s += *p; } return s; }
答え:未定義動作(undefined behavior)。C/C++こわい(((;´・ω・`))) これがC/C++だ( ・`ω・´)キリッ。
C/C++では、ポインタ値は “有効な配列要素”*2 もしくは “配列末尾要素の次位置(one past the last element)” しか指してはならない。それ以外の値、例えば前掲コードのように配列末尾要素の2つ後ろ(のつもり)などは、加減算によりポインタ値が算出された時点で未定義動作と規定される。
C
C11 6.5.6/p8より引用(下線部は強調)。C99でも6.5.6/p8。
8 When an expression that has integer 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 integer 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
(whereN
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 expressionP
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 expressionQ
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. If the result points one past the last element of the array object, it shall not be used as the operand of a unary*
operator that is evaluated.
C++
C++17 8.7/p4より引用(下線部は強調)。C++14/11/03では5.7/p5。
4 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 elementx
[i] of an array objectx
with n elements, the expressionsP + J
andJ + P
(whereJ
has the value j) point to the (possibly-hypothetical) elementx
[i+
j] if 0 ≤ i+
j ≤ n; otherwise, the behavior is undefined. Likewise, the expressionP - J
points to the (possibly-hypothetical) elementx
[i-
j] if 0 ≤ i-
j ≤ n; otherwise, the behavior is undefined.
関連URL