配列にポインタ・アクセスする
C/C++では、配列にポインタでアクセスする方法がよく使われていたが、C#でも可能である。以下はそれを記述した例である。
1: using System;
2:
3: namespace ConsoleApplication1
4: {
5: class Class1
6: {
7: private static int test1( int [] array )
8: {
9: int sum = 0;
10: for( int i=0; i<array.Length; i++ )
11: {
12: sum += array[i];
13: }
14: return sum;
15: }
16: unsafe private static int test2( int [] array )
17: {
18: int sum = 0;
19: fixed( int * pArray = &array[0] )
20: {
21: int * ptr = pArray;
22: for( int i=0; i<array.Length; i++ )
23: {
24: sum += *ptr;
25: ptr++;
26: }
27: }
28: return sum;
29: }
30: static void Main(string[] args)
31: {
32: int [] array = { 2, 4, 3, 1, 5 };
33: int sum1 = test1( array );
34: Console.WriteLine(sum1);
35: int sum2 = test2( array );
36: Console.WriteLine(sum2);
37: }
38: }
39: } |
|
配列をポインタでアクセスするサンプル・プログラム5 |
ポインタによる配列のアクセスでは、配列の先頭の要素へのポインタを取得し、そのポインタを進めることによって各要素の値にアクセスすることができる。 |
これを実行すると以下のようになる。
|
サンプル・プログラム5の実行結果 |
インデックスとポインタによる2通りのアクセス方法で、配列の各要素の値の合計値を求め表示している。
|
7〜15行目は、unsafeコードを使わないメソッドで、16〜29行目は同じ結果を出すようにunsafeコードを用いて記述した例である。配列は移動型なので、19行目のfixedステートメントで一時的に固定している。20〜27行目の範囲内なら、ポインタ変数をpArrayを通じて、配列にアクセスすることができる。よく見ると、19行目で指定されているのは、配列の0番目の要素だけである。しかし、配列は1つの塊として、物理的に連続したメモリ上に確保されているので、1個の要素を固定すればすべての要素も同時に固定されるのである。25行目のように、ポインタにも“++”や“--”演算子が使用できる。
ポインタのサイズ
ポインタには、“++”や“--”演算子が利用できるが、その際、変化する量はデータ型によって異なる。データ型によって占有するサイズが異なるので、“++”演算子で次のデータに進めたとき、進む量を変化させる必要があるためだ。以下は具体的なサイズを、sizeof演算子で調べるサンプル・ソースである。
1: using System;
2: using System.Runtime.InteropServices;
3:
4: namespace ConsoleApplication10
5: {
6: [StructLayout(LayoutKind.Sequential,Pack=1)]
7: struct SampleStruct1
8: {
9: public int x;
10: public sbyte y;
11: }
12: [StructLayout(LayoutKind.Sequential,Pack=2)]
13: struct SampleStruct2
14: {
15: public int x;
16: public sbyte y;
17: }
18: [StructLayout(LayoutKind.Sequential,Pack=4)]
19: struct SampleStruct3
20: {
21: public int x;
22: public sbyte y;
23: }
24: class Class1
25: {
26: unsafe static void test()
27: {
28: Console.WriteLine( sizeof(int) );
29: Console.WriteLine( sizeof(short) );
30: Console.WriteLine( sizeof(sbyte) );
31: Console.WriteLine( sizeof(SampleStruct1) );
32: Console.WriteLine( sizeof(SampleStruct2) );
33: Console.WriteLine( sizeof(SampleStruct3) );
34: }
35: static void Main(string[] args)
36: {
37: test();
38: }
39: }
40: }
|
|
ポインタのサイズを求めるサンプル・プログラム6 |
ポインタのサイズはsizeof演算子を使用して求めることができる。structへのポインタに対してはStructLayout属性でメンバの値を配置するサイズを指定することができる。 |
これを実行すると以下のようになる。
|
サンプル・プログラム6の実行結果 |
intのサイズは4bytes、shortのサイズは2bytes、sbyteのサイズは1byteとなった。structの場合には同じメンバの構成であってもStructLayout属性での指定によってサイズが異なる。
|
まず表示内容の最初の3行は簡単に分かるだろう。intのサイズは4bytes、shortのサイズは2bytes、sbyteのサイズは1byteであることを示している。つまり、“++”演算子で、int型のポインタなら4個進む、というわけだ。問題は後半の3つの数値だろう。これは、すべて同じ内容の3つのstructのサイズを表示しているが、すべて結果が異なっている。これは、StructLayout属性のPack引数の指定により変化したものだ。Pack引数は、メンバの値を配置する単位を指定する。“Pack=4”なら、4bytesごとに整列させられるので、1byteで済むsbyteも、4bytes分の場所を占有してしまい、struct全体では8bytesを消費してしまっている。一方、“Pack=1”なら1byteごとに配列するので、単純にint型(4)とsbyte型(1)で計5bytesという結果になるわけである。
Insider.NET 記事ランキング
本日
月間