第21章 ポインタを使用できる「安全でないコード」:連載 改訂版 C#入門(1/4 ページ)
連載最終回となる今回は、C#におけるポインタの利用について解説する。ポインタのサポートにより、C#では低レベルな処理も効率よく行うことができる。
本記事は、(株)技術評論社が発行する書籍『新プログラミング環境 C#がわかる+使える』から許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。
低レベルの処理を効率よく行うためにポインタは有効な機能である。しかし、強力である反面、トラブルのもとにもなりやすく、必ずしも利用を推奨できるものではない。本章ではC#からポインタを利用する方法を解説する。
21-1 「安全でないコード」とは何か?
本章で扱う「安全でないコード」は、必ずしもすべてのC#プログラマーが知っているべき機能ではない。この機能を必要とするプログラマーはそれほど多くはないだろう。平均的プログラマーなら知らなくても問題はないし、不必要に使うと問題をややこしくするので、むしろ知らない方がよいとすらいえる。なお、Win32 APIの呼び出しは、前章で説明したとおり、「安全でないコード」とは関係なく実行できるので、「安全でないコード」の知識抜きでもほとんど問題はない。そのため、「安全でないコード」は難しすぎると思ったら、読み飛ばしても問題はない。なお、本章は、C/C++言語でいうところのポインタに関する知識を前提としている。
プログラミング言語には低級と高級がある。といっても、決して常に高級言語が優れているというわけではない。より生のコンピュータの構造に近いものが低級であり、より人間に近いものが高級と呼ばれるわけである。当然、人間に近い言語の方が書きやすいが、コンピュータの性能を限界まで絞り出すには、生のコンピュータの構造を意識できる言語の方が都合よい。1980年代のC言語の大流行は、人間に近い言語でありながら生のコンピュータの構造を意識しやすいという特徴から起こったものだといえる。しかし、コンピュータの性能が向上することにより、あまり生の構造を意識しなくても、十分な性能が得られるようになってきて、できるだけ生の構造をプログラマーに見せないプログラミング言語が好まれるようになってきた。例えば、JavaはCと似た構文でありながら、生の構造が全くプログラマーから見えないように配慮されている。またBASIC言語でも、かつてはPEEK、POKEといった生の構造に触れる機能が必須であったが、現在のVisual Basicにそれらの機能は備わっていない。そのような世の中の流れから考えれば、プログラマーがコンピュータの生の構造に触れる必要はほとんどなく、むしろ生の構造が見えることはトラブルの発生原因になるだけといえる。
しかし、必要がほとんどなくなったのは事実ではあるが、全くなくなったわけではない。まれに、生の構造を意識する必要が生じる場合がある。実現したい機能そのものが生の構造に依存する場合や、そうしなければ十分な処理速度が得られない場合が存在する。そのような非常にまれなケースに対応するために、C#には「安全でないコード」という機能が用意されている。
「安全でないコード」は、メモリに関する生の構造をプログラマーが直接扱う方法を提供する。C#プログラムは、メモリを自動的に管理する .NET FrameworkのManagedコード(マネージ・コード)として実行され、メモリ管理はすべて .NET Frameworkに任せられている。この結果、C#プログラマーはメモリをほしいときにほしいだけ確保するようにコードを書くだけで、すべて問題なく稼働する。不要になったメモリは自動的に回収され、インスタンスなどは必要に応じて移動され、整理される。「安全でないコード」は、この自動メモリ管理機構を停止させたり、無視するものではない。そうではなく、このメモリ管理機構で許される範囲内で、生の構造を意識することを許す機構といえる。そのため、何もかも好き勝手に生の構造をいじれるわけではない。メモリ管理機構に影響を与えるような操作を行う場合は、行儀よくその旨をコード上で明らかにしなければならない。
さて、最も簡単な「安全でないコード」を用いた例をList 21-1に示す。
1: using System;
2:
3: namespace Sample001
4: {
5: class Class1
6: {
7: unsafe private static void test()
8: {
9: int i=123;
10: int * p = &i;
11: Console.WriteLine(*p);
12: }
13: [STAThread]
14: static void Main(string[] args)
15: {
16: test();
17: }
18: }
19: }
このコードをビルドする場合には、コンパイラに/unsafeオプションを付けるか、Fig.21-1のように、Visual Studio .NETのプロジェクト設定で、[セーフモード以外のコードブロックの許可]を[True]に変更しておく必要がある。
わざわざ指定しなければ使えないということは、安全でないコードは、よほどのことがなければ使うべきではないことを意味している。通常の方法で実現可能なら、「安全でないコード」は使わない方がよい。
これを実行するとFig.21-2のようになる。
ソース・コードは、C/C++プログラマーなら説明するまでもないだろう。10行目の「int *」とは、整数へのポインタを示すデータ型である。&iの&記号は参照を行う演算子で、変数iを参照するアドレス値を得る。そして、11行目の*pは、ポインタ変数pによって参照されるメモリの内容を取り出すことを示す。回りくどいが、結果的に変数iの内容、つまり123という値が出力されることになる。
もちろん、ただ単にC#のソースを書いても、ポインタは使用できない。10〜11行目でポインタを記述してもエラーにならないのは、7行目のメソッド宣言にunsafeキーワードが付加されることにより、このメソッドでの「安全でないコード」の利用が許可されたためだ。当然のことながら、unsafeキーワードはtestメソッドにしか付加されておらず、Mainメソッドには付加されていないので、Mainメソッド内でポインタを記述すればエラーになる。
21-2 さまざまなものに付加できるunsafeキーワード
List 21-1ではunsafeキーワードをメソッドに付加したが、ほかにも、クラス、構造体、デリゲート、インターフェイスなど、さまざまなものに付加することができる。List 21-2は、クラスとメソッドにunsafeキーワードを付けてみた例である。
1: using System;
2:
3: namespace Sample002
4: {
5: class SafeClass
6: {
7: unsafe static public void test()
8: {
9: int i=123;
10: int * p = &i;
11: Console.WriteLine(*p);
12: }
13: }
14: unsafe class UnsafeClass
15: {
16: static public void test()
17: {
18: int i=123;
19: int * p = &i;
20: Console.WriteLine(*p);
21: }
22: }
23: class Class1
24: {
25: [STAThread]
26: static void Main(string[] args)
27: {
28: SafeClass.test();
29: UnsafeClass.test();
30: }
31: }
32: }
これを実行するとFig.21-3のようになる。
ここで注目していただきたいのは、14行目でUnsafeClassクラスに付加されたunsafeキーワードである。このキーワードの効力は、クラス内全体に及ぶ。そのため、16行目のtestメソッドの宣言ではunsafeキーワードを付ける必要はない。このメソッドは内部でポインタを使用しているが、メソッドそのものがunsafeであると示す必要はない。そこが、7行目にあるunsafeではないクラス内のtestメソッドとの違いだ。
Copyright© Digital Advantage Corp. All Rights Reserved.