C言語の関数と同じ動作を、C#言語で実現しようとした場合のTipsです。


文字列比較

Basic言語などの様に
if ( str1 == str2 )
として、同一の文字列か比較できる。
また、strcmpの様な、文字列の大小比較は、stringクラスのCompareメソッドや、CompareToメソッドを使う。
if ( str1.CompareTo(str2) == 0 )
or
if ( string.Compare(str1, str2) == 0 )
Compareメソッドは、staticメソッドなので、比較元(str1)の文字列変数がnullであっても例外が発生しないところがJavaと比較したときの優位な点かな。

なお、stricmpの様に、英大文字小文字を同一視した比較は、
if ( string.Compare(str1, str2, true) == 0 )
とすれば可能。
また、Compareメソッドは、カルチャ(いわゆる地域化)に対応した文字列の大小比較も出来るらしい。


memcpy

C言語で、char配列のmemcpyに相当する処理を行うには、System.ArrayクラスのCopyメソッドを使う。
ただし、クラスのデータをメモリに展開するような用途には使えない。

Copyメソッドstaticメソッドなので、Bufferクラスのインスタンスを作成する必要は無い。
memcmp(dst, src, len);
Array.Copy(src, dst, len);

また、C言語では、ポインタを使ってメモリブロックの任意の位置を指し示す事が簡単に出来るが、C#言語では、一般的にはポインタを意識させない作りなので、メモリブロックの任意の位置を指定してコピーさせる場合、ConstrainedCopyメソッドを使う。
ConstrainedCopyメソッドもstaticメソッドなので、Bufferクラスのインスタンスを作成する必要は無い。
memcmp(&dst[10], &src[20], len);
Array.ConstrainedCopy(src, 20, dst, 10, len);
ただし、この方法でもポインタを使うことは出来ないらしい。

なお、byte型などのプリミティブ型の配列の場合、System.BufferクラスのBlockCopyメソッドを使った方が処理が速いらしい。
BlockCopyメソッドもstaticメソッドなので、Bufferクラスのインスタンスを作成する必要は無い。
memcmp(dst, src, len);
Buffer.BlockCopy(src, 0, dst, 0, len);

memcmp(&dst[10], &src[20], len);
Buffer.BlockCopy(src, 20, dst, 10, len);


realloc

(厳密には違うかも知れないが…)
一度確保したメモリブロックの大きさを変更する場合、System.ArrayクラスのResizeメソッドを使う。
byte [] arr = new byte[10];
Array.Resize<byte>(ref arr, 20);
の様な使い方をするらしい。
なお、
byte [] arr = null;
Array.Resize<byte>(ref arr, 20);
でも例外は発生せず、望み通りの結果が得られる。


memcmp

無いの…???
Microsoftのサーポートページ(JP307020)
の「2 つのハッシュ値の比較」によれば、
2. 2 つのバイト配列を比較する最もわかりやすい方法は、各要素を第 2 の値の対応する要素と比較しながら配列全体をループすることです。
だそうだ。

文字列中にNULL文字があっても構わないので、バイト配列であれば、Latin-1の様な8bitの1バイト文字に見立てて、
string str1 = Encoding.GetEncoding(850).GetString(arr1);
string str2 = Encoding.GetEncoding(850).GetString(arr2);
if (  str1 == str2  )
の様に文字列として比較するとか…。って、頭悪すぎる。
世間的には、memcmpの様な処理が標準で存在する言語って珍しいような気もするのだけど、やっぱり必要でしょ???


memset

Clearメソッドで0クリアできるから無い????
確かに、C言語でmemsetを使うケースの大多数は0クリアのような気がするけど、それって、auto変数の中身が初期化されないC言語固有の問題では?


書式指定

printfなどの書式指定は、Formatメソッドを使用するが、C言語の書式指定に比べて強力なものとなっている。
printf("0x%x(%d)\n", addr, addr);
は、
System.Console.WriteLine(string.Format("0x{0:X}({0})", addr));
と、同じ値を複数出力したい場合に簡潔に記述できる。

また、書式指定の指定では、
C言語 C#言語
"%d\n" "{0}" or "{0:D}"
"%5d\n" "{0,5}" or "{0,5:D}"
"%-5d\n" "{0,-5}" or "{0,-5:D}"
"%05d\n" "{0:D5}"
"%s\n" "{0}"
"%10s\n" "{0,10}"
"%-10s\n" "{0,-10}"
"%8.2f\n" "{0,8:F2}"
"%.2f\n" "{0:F2}"
など、C言語で出来る事は一通り出来そう。

また、書式指定文字列を動的に作成するときなど、書式指定文字列の中に中括弧を入れたい場合は、中括弧を二重に記述する。
string.Format("{{0:{0}}}", "D");
の実行結果は、
"{0:D}"
となる。


文字列の数値変換

C言語では、
  • 基数を指定しない数値変換 atoi atol atof
  • 基数を指定する数値変換 strtol strtoul strtod
などの文字列を数値に変換するための関数がある。

このとき、文字列として、10進数("123")や、0xを先頭につけた16進数("0x123")の文字列を一つの処理で変換させようとすると、strtoXXX関数の基数を0として
long l = strtol("0x123", 0);
の様に記述すると正しく数値変換できる。(MSC拡張仕様???)

ところが、
  • Convert.ToInt32("0x123", 10); は、System.FormatException例外が発生
  • Convert.ToInt32("0x123", 0); は、System.ArgumentException例外が発生
となり、上手く行かない。

結局、先頭の文字列を見て、基数を別途指定するようにしないといけない???

なお、
  • Convert.ToInt32("0x123", 16);
  • Convert.ToInt32("123", 16);
のどちらも、0x123(291)と変換する。
最終更新:2008年01月04日 01:37