この記事は、C言語 Advent Calendar 2016の11日目の記事です。


前回の記事、十六進数で文字列に変換する (Boostのhex関数)では、uint32_t型の変数xを定義し、&x + 1というポインタ演算を行うC++のコードを載せました。xは配列でもなんでもありません。果たしてこんなことして良いのでしょうか、というのが今回の話です。Cにも当てはまる話なので、C言語 Advent Calendarに乗っかせていただくことにしました。

結論を述べると、問題ありません。C11ドラフトであるISO/IEC 9899:201x – n1548.pdfの6.5.6 Additive operatorsの7に、1要素の配列と同じよう取り扱う旨が書かれています。なお、Additive operatorsという見出しですが、2項演算子の+と-両方について書かれているところです。

For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

なお、C++でも同様の規定があります。C++14ドラフトのC++ International Standard – n4296.pdfでは、5.3.1 Unary operators [expr.unary.op]の3の途中に、以下の文章があります。

For purposes of pointer arithmetic (5.7) and comparison (5.9, 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.

このように、CおよびC++でそれぞれ、&x + 1は、配列の最後の要素の次を指すポインタと同様、*で参照剥がしさえしなければ問題ありません。

こんなコードは全然問題ないわけです。

#include <stdio.h>
 
void test1() // 例
{
  int x = 500;
  int* end = &x + 1;
  for (int* p = &x; p != end; p++)
  {
    printf("%d\n", *p);
  }
}
 
void test2() // 比較用に配列を使ったもの
{
  int a[] = {
    1,
    2,
    3,
  };
  int* end = a + sizeof a / sizeof a[0];
  for (int* p = a; p != end; p++)
  {
    printf("%d\n", *p);
  }
}
 
int main()
{
  test1();
  puts("--------");
  test2();
}

ちなみに、Stack overflowにもIs the “one-past-the-end” pointer of a non-array type a valid concept in C++?という質問がありました。

2017年2月18日追記: 以下のライブラリで、この仕様が使われています。

pstade::oven::single
/trunk/pstade/pstade/oven/single.hpp
上記の移植であるOvenToBoostのboost::range::single
OvenToBoost/single.hpp at master · faithandbrave/OvenToBoost

というわけで、C++のコードを書いていて気になったことですが、Cにも当てはまることだったので、C言語 Advent Calendarのネタといたしました。


スポンサード リンク

この記事のカテゴリ

  • ⇒ 配列でないオブジェクトに対するポインタ演算
  • ⇒ 配列でないオブジェクトに対するポインタ演算