やっと理解したこと(VC++)(’14/10)
今回、1からVC++を再独習している中で、ずっと十分に理解できていなかったこと
が一つ一つ、やっと私の中で理解できつつあります。
一方で、以前有していたVC++2002.NETから変わってしまったVC++環境で戸惑い
なども出てきてますが、、息子にアドバイスを受けながら少しずつ慣れつつありま
す。
前者では、本当に基本的なお恥ずかしいようなことがわかっていなかったことの一
つに関数のパラメータがあり、本当に今やっとこさわかったところです。
そして、それは「*」とか「&」が右辺にあるときと左辺にあるときで、意義が異なるこ
と、左辺にあるときはどういうものなのかということがやっと理解できたことからの結
果でした。
実はこれが本当にわかっていなかったため、msdnライブラリにある関数のパラメー
タ部がよく理解できていなかったんですね。ですから、これまでどうしてきたかとい
うと、本やネットのプログラム例を見て、真に理解できてないまま、ここはこれをい
れればいいのかと真似ていただけ・・・私はそういうのは自分にとってはブラックボッ
クスが嫌いで、反プラグマティズムというポリシーに反するものだったんですが・・・
関数定義においては、
<データ型> 関数名(<データ型>変数、・・・)
{
・・・・・・
}//@関数定義
という形になっていて、msdnライブラリでは、
<データ型> 関数名(
<データ型> 変数,
・・・・・・
);//Amsdnライブラリの関数表記
と書かれています。Aは@の( )内のパラメータに相当するわけです。
で、私はこれを根本的に思い違いしてきたというわけです。
私が今頃、やっとこさ得た結論は・・・
変数宣言/初期化における、
<データ型> 変数 = 初期化子;//B
と同じだということでした。
すなわち、
関数定義の各パラメータ;Bの左辺
関数呼び出し側パラメータ;Bの右辺の初期化子
ということに気が付いたということです。これは単なる値渡しの場合だけなら気にも
しなかったんですが、msdnライブラリで「*」とか「&」が出てくる場合の理解として重
要な感がしています(そんな思いをしているのは私だけかもしれませんが)。
Bの形においては、
(a)<データ型> *変数=ポインタ(アドレス)
(ex. &変数、&構造体名、配列名など);
(b)<データ型> &変数=変数;
というのが出てきます。
(a)の左辺は変数が<データ型>の値を指し示すポインタ変数ですから、右辺
の初期化子はポインタ(値が入るメモリアドレス)となります。したがって、関数のパ
ラメータが「<データ型> *変数」とあるなら、関数呼び出し側ではパラメータとして
Bの右辺の初期化子に相当するポインタ(値が入るメモリアドレス)を記入すればよ
いということになります。
一方、(b)の方は、「参照(リファレンス)」と称せられるもので、左辺の変数は右
辺の変数の別名を表すという意味になります。私は右辺に使われる間接参照演算
子「&」(アドレス演算子)と完全に混同してました。ポインタとは無関係なものです。
このように理解できたため、やっと、msdnライブラリで目にした"wcscpy_s"という関数
のテンプレートの方の形
errno_t wcscpy_s(
wchar_t (&strDestination)[size],
const wchar_t *strSource
); // C++ only
の第一パラメータ"wchar_t (&strDestination)[size]"の意味と呼び出し側に何
を入れればいいのかがやっとこさ理解できたというわけです。
配列の場合はややこしくて、この[size]の前が(&xxxx)と( )で囲まれているとき、
これは「[size]の大きさの配列への参照」すなわち、「事前に宣言された[size]
の大きさの配列の名の別名」という意味となろうかと思います。ですから、呼び出し
側では「事前に宣言された[size]の大きさの配列名」を記入すればよい」
ということになるわけです。
ちなみに、msdnライブラリでこのように、"(&strDestination)[size]"と書かれている
意味は、
事前に必ず、 <データ型> 配列名[size] の宣言が必要
ということだったんですね。だからこそ、
LPTSTR 変数; //変数宣言
変数=(LPTSTR)malloc(確保メモリサイズ);
の変数ではwcscpy_sのパラメータ二つのテンプレートの方ではダメというわけだっ
た(コンパイルはできますが、ランタイムエラーになります)理由がやっとわかりまし
た。
そして、改めて"lstrcpy"という関数の存在意義が理解できました。
LPTSTR pText;
LPCTSTR pString;
pText=(LPTSTR)malloc(size);
とあって、pStringの文字列をpTextにコピーするときは、
lstrcpy(pText , pString);
と"lstrcpy"を用いるのがベターですね。実は私、lstrcpyは小文字のため、Cの標
準関数だと思い込んでいたのですが、Wins32APIだったんですねぇ。
改めて、Windowsプログラミングに特化するなら、Win32APIを使うのがベターだと感
じました。Cの標準関数はしっかりC/C++を理解できていないと問題を起こすことも
よくわかりました。
さて、vc++2013 Express(製品版も)のコンパイラがデフォではUNICODE対応のた
めに文字列操作で色々と戸惑ってきましたが、対応関数の知識だけでは不十分で
あることがファイル入出力を学び、プログラムを書いてみてわかりました。
ファイル入出力ですが、いくつも方法があることがわかりましたが、個人的趣味の
世界ゆえ、とりあえずWin32APIのものを学びました。すなわち、CreateFile、
ReadFile、WriteFile、GetFileSizeなどの関数群を使う方法です。
そして、合わせてcommon dialogについても学び一緒に試してみました。
その中でわかったことがありました。私が使っているEmEditorやWindows標準添付
のメモ帳で書いた.txtデータがそのままでは文字化けしてしまうことでした。
これは、すぐにEmEditorやWindows標準添付メモ帳はデフォではANSI保存だから
だとわかったのですが、メモ帳でUNICODE保存したとき、テストプログラムではもう
一つ問題があることがわかりました。後ろに変な文字化けした一連のものがついて
しまう・・・
当初、mallocで確保した読み込みバッファサイズをGetFileSizeの結果の値としてい
たのです。それは、この関数の戻り値が「(文字数+1)×sizeof(TCHAR)」であり、
この+1は文字列最後の'\0'だろうと判断したためでした。
結果は、息子にコードと結果をチェックしてもらって彼から得たヒントとネットで調べ
たことでわかりました。メモ帳の場合、UNICODEのときは、文字列の先頭にそれを
認識する2byteのBOMというのが入るということだそうです。したがって、先の
mallocで確保すべきサイズは少なくとも(GetFileSize戻り値+2)としておく必要があ
るということでした。コードをそのように変更したら無事UNICODEで保存したメモ帳
の内容を表示することができました。
それにしても気になりましたのは、GetFileSizeという関数。Win32APIですが、msdn
ライブラリには、ただ「ファイルサイズを取得する」とだけ書かれています。
文字列数を取得するGetWindowTextlengthとかwcslenという関数の場合は明確に、
「最後の'\0'は数に含まない」と注意書きがあります。ですから、GetFileSizeは
(文字数+1)×sizeof(TCHAR)だと思っていたのですが・・・。
どうやら、私の知識不足というか理解不十分なところがところがあるようですね。
TEXT("xxxx")というプログラム内に埋め込まれた文字列には最後に自動的に'\0'
がつくと思っていたのですが、キャリッジリターンをしていないエディタのtxt文書で
は自動的には'\0'が最後についていないということなんですね??
こんな知識まで必要だとは思いもよりませんでした。奥が深いですねぇ。
ところで、ネットでC言語に関して調べていて気が付いたのは、Win32APIネイティブ
プログラミングに特化したサイトは本当に少ないですね。VC++というと大半は、
MFCばかり。前に所有していたVC++2002.NETでMFCは見たことはありました(本も
購入しました)が、「プログラミング」ということでの私の「趣味」思考からは程遠い代
物だとわかり、学ぶのを放棄したものなんですね。
で、VC++でないと、ネイティブなC/C++の解説ばかり・・・
Windowsだけ相手にしているわけではないプロの方にとってはそれが「普通」なんで
しょうが、かつてMS-DOS時代にQCを独学でかじっていた(ほとんどものにならな
い前に挫折し飽きてしまった)私にとっては、今更、"void main(void)"で始まるコード
とか黒画面に白文字の出力画面などとはという感がどうしてもしてしまいます(^^;
ものにできる前に飽きてしまって放棄してきたんですが、Windows98時代はVC++6.0、
そしてWindowsXP時代にはVC++2002.NETを購入していろいろ本を読んできた私で
すので。
今回、無料で入手したVisual Studio2013 Expressですが、これについているVC#と
かVBは今のところ全く興味なしです。これらは、所謂「高水準言語」なんですよね。
「プログラム造り」そのものが目的なら便利でしょうが、古き学生時代、電気専攻に
も拘わらずコンピュータプログラミングが大嫌いだった私でしたが、そんな私にその
プログラミングに興味を湧せ、パソコン所有者にした原点はBasicでした。これはま
さに「高水準(高級)言語」だったんですね。しかし、それではハードから離れすぎ
ているということで、QCをかじり、さらにはアセンブラのまで手を出そうとした私
(¥35000も出して買ったMASM6.0は1週間でぼうらかしてしまいましたが(^^;)には
そういうブラックボックスだらけのものは今更手を出そうという意欲はわいてこない
からです。簡単な基本的プログラミングでWindowsに特化したC++言語(VC++)の
ネイティブプログラミングが私には一番わかりやすく楽しいということです。
尚、私の過去のプログラミング学習/コード履歴というのは、
(会社での業務関連での技術計算用/コンピュータ対応)FORTRAN⇒TSS
⇒業務関連技術計算用:パソコンBasic⇒
(以後、趣味の世界)PC98対応遊びでの88-Basic(Disk Basic ⇒MS-DOS Basic)
⇒QC(+ちょこっとQB)⇒VC++6.0⇒VC++2002.NET⇒VC++2013 Express
です(Windows98時代にVBも少しだけ試した記憶があります)。
戻る