連載

C#入門

第21回 ポインタを使用できるunsafe

(株)ピーデー
川俣 晶
2002/02/16


unsafeコードとは何か

 今回扱うunsafeコードは、必ずしもすべてのC#プログラマが知っているべき機能ではない。この機能を必要とするプログラマはそれほど多くはないだろう。平均的プログラマなら知らなくても問題はないし、不必要に使うと問題をややこしくするので、むしろ知らない方がよいとすらいえる。なお、Win32 APIの呼び出しは、前回説明したとおり、unsafeコードとは関係なく実行できるので、unsafeコードの知識抜きでもほとんど問題はない。そのため、unsafeコードは難しすぎると思ったら、読み飛ばしても問題はない。なお、今回は、C/C++言語でいうところのポインタに関する知識を前提としている。

 プログラム言語には低級と高級がある。といっても、決して常に高級言語が優れているというわけではない。より生のコンピュータの構造に近いものが低級であり、より人間の志向性に近いものが高級と呼ばれるわけである。当然、人間の志向性に近い言語の方が書きやすいが、コンピュータの性能を限界まで絞り出すには、生のコンピュータの構造を意識できる言語の方が都合良い。1980年代のC言語の大流行は、人間の志向性に近い言語でありながら、生のコンピュータの構造を意識しやすいという特徴から起こったものだといえる。しかし、コンピュータの性能が向上することにより、あまり生の構造を意識しなくても、十分な性能が得られるようになってきて、できるだけ生の構造をプログラマに見せないプログラム言語が好まれるようになってきた。例えば、JavaはCと似た構文でありながら、生の構造がまったくプログラマから見えないように配慮されている。またBASIC言語でも、かつてはPEEK、POKEといった生の構造に触れる機能が必須であったが、現在のVisual Basicにそれらの機能は備わっていない。そのような世の中の流れから考えれば、プログラマがコンピュータの生の構造に触れる必要はほとんどなく、むしろ生の構造が見えることはトラブルの発生原因になるだけといえる。

 しかし、必要がほとんどなくなったのは事実ではあるが、まったくなくなったわけではない。まれに、生の構造を意識する必要が生じる場合がある。実現したい機能そのものが生の構造に依存する場合や、そうしなければ十分な処理速度が得られない場合が存在する。そのような非常にまれなケースに対応するために、C#にはunsafeコードという機能が用意されている。

 unsafeコードは、メモリに関する生の構造をプログラマが直接扱う方法を提供する。C#プログラムでは、.NET Frameworkはメモリを自動的に管理するマネージド・コードとして実行され、メモリ管理はすべて.NET Frameworkに任せている。この結果、C#プログラマはメモリを欲しいときに欲しいだけ確保するようにコードを書くだけで、すべて問題なく稼働する。不要になったメモリは自動的に回収され、インスタンスなどは必要に応じて移動され、整理される。unsafeコードは、この自動メモリ管理機構を停止させたり、無視したりするものではない。そうではなく、このメモリ管理機構で許される範囲内で、生の構造を意識することを許す機構といえる。そのため、何もかも好き勝手に生の構造をいじれるわけではなく、メモリ管理機構に影響を与えるような操作を行う場合は、お行儀良くその旨をコード上で明らかにしなければならない。

 さて、最も簡単なunsafeコードを用いた例を以下に示す。

 1: using System;
 2:
 3: namespace ConsoleApplication2
 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:     static void Main(string[] args)
14:     {
15:       test();
16:     }
17:   }
18: }
最も簡単なunsafeコードを用いたサンプル・プログラム1
メソッド宣言に“unsafe”を付けることによって、そのメソッド内でポインタが使用可能となる。

 このコードをビルドする場合には、コンパイラに「/unsafe」オプションを付けるか、Visual Studio .NETのプロジェクト設定で、unsafeコードブロックの許可を「True」に変更しておく必要がある。

[プロパティページ]ダイアログ
[ソリューションエクスプローラ]でプロジェクトを選択した後、[プロジェクト]メニューの[プロパティ]で開く。Visual Studio .NETでunsafeコードを使用するためには、[unsafeコード ブロックの許可]を「False」から「True」に変更する必要がある。

 わざわざ指定しなければ使えないということは、unsafeコードは、よほどのことがなければ使うべきではないことを意味している。繰り返しになるが、通常の方法で実現可能なら、unsafeコードは使わない方がよい。

 これを実行すると以下のようになる。

サンプル・プログラム1の実行結果
サンプル・プログラム1の実行結果、整数値123の入った変数へのポインタが示す内容を表示している。

 ソースコードは、C/C++プログラマなら説明するまでもないだろう。10行目の“int *”とは、整数へのポインタを示すデータ型である。“&i”の&記号は参照を行う演算子で、変数iを参照するアドレス値を得る。そして、11行目の“*p”は、ポインタ変数pによって参照されるメモリの内容を取り出すことを示す。回りくどいが、結果的に変数iの内容、つまり“123”という値が出力されることになる。

 もちろん、ただ単にC#のソースを書いても、ポインタは使用できない。10〜11行目でポインタを記述してもエラーにならないのは、7行目のメソッド宣言にunsafeキーワードが付加されることにより、このメソッドでのunsafeコードの利用が明示的に許可されたためだ。当然のことながら、unsafeキーワードはtestメソッドにしか付加されておらず、Mainメソッドには付加されていないので、Mainメソッド内でポインタを記述すればエラーになる。

さまざまなものに付加できるunsafeキーワード

 上記の例ではunsafeキーワードをメソッドに付加したが、ほかにもいろいろなものに付加できる。クラス、struct、delegate、interfaceなど、さまざまなものに付加することができる。以下は、クラスとメソッドにunsafeキーワードを付けてみた例である。

 1: using System;
 2:
 3: namespace ConsoleApplication5
 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:     static void Main(string[] args)
26:     {
27:       SafeClass.test();
28:       UnsafeClass.test();
29:     }
30:   }
31: }
クラスにunsafeキーワードを付けたサンプル・プログラム2
クラスをunsafeと指定した場合には、その効力はクラス全体に及ぶ。

 これを実行すると以下のようになる。

サンプル・プログラム2の実行結果
unsafeが指定されたクラス内のメソッドはポインタが使用可能だ。

 ここで注目していただきたいのは、14行目でUnsafeClassクラスに付加されたunsafeキーワードである。このキーワードの効力は、クラス内全体に及ぶ。そのため、16行目のtestメソッドの宣言ではunsafeキーワードを付ける必要はない。このメソッドは内部でポインタを使用しているが、メソッドそのものがunsafeであると示す必要はない。そこが、7行目にあるunsafeではないクラス内のtestメソッドとの違いだ。

 

 INDEX
  第21回 ポインタを使用できるunsafe
  1.unsafeコードとは何か
    2.固定型と移動型
    3.配列にポインタ・アクセスする
    4.ポインタを演算する
    5.本当にunsafeコードが欲しくなる事
 
「C#入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間