yohhoyの日記

技術的メモをしていきたい日記

配列有効範囲外を指すポインタ値は存在が許されない

プログラミング言語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 (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. 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 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 + jn; otherwise, the behavior is undefined. Likewise, the expression P - J points to the (possibly-hypothetical) element x[i - j] if 0 ≤ i - jn; otherwise, the behavior is undefined.

関連URL

*1:要出典。C言語 == 高級アセンブラ という認識では危ないというお話。

*2:ポインタ値が指す先は、当然 int など非配列のデータ型オブジェクトでも良い。