はじめに
多くの人は「C言語で開発してるけど環境がVSだから.cppになっているだけだよね」と考えているかと思いますが、私たちがC言語の書き方に固執しているだけであって開発環境は紛れもなくC++なんです。とはいえ委縮することはありません。C++でもC言語プログラミングは問題なく可能ですから。
VS開発の際の面倒なこと
すごく地味ですが我々C言語民にとって非常に煩わしいのがファイル操作関数と文字列操作関数はセキュリティ強化版を使えというVSからの指令。警告というかたちで表示されなんとも鬱陶しい。
例えばファイル操作関数の場合だと#pragma warning( disable : 4996 )
をファイルの最初に記述すれば事足りるのですがこれはあまりお勧めしません。
警告されるということは何らかの問題があるということなので警告レベルを最大にしたコンパイルで警告ゼロになるようなコーディングをしましょう。
主な文字列操作関数
今回はstrcpy()
とstrtok()
の説明をしようと思います。目先で使ったのがこれだっただけなので深い意味はありませんが他の関数も知りたいときはリクエストを送ってもらえれば対応するかもです。
_sはセキュリティ強化版であることを意味しています。VSではセキュリティ強化版でないとコンパイルエラーが出ます。
# include <stdio.h>
# include <string.h>
int main(void)
{
char source[81] = "これは、サンプル、です";
char test[81];
char *testPtr;
strcpy(test, source);
puts(test);
testPtr = strtok(source, "、");
puts(testPtr);
testPtr = strtok(NULL, "、");
puts(testPtr);
testPtr = strtok(NULL, "、");
puts(testPtr);
return 0;
}
BorlandC++ Compilerではこのような書き方を用いることが多いかと思いますがVSは許してはくれません。
# include <stdio.h>
# include <string.h>
int main(void)
{
char source[81] = "これは、サンプル、です";
char test[81];
char *testPtr;
char *w; // strtok_sの第3引数
strcpy_s(test, strlen(source), source);
puts(test);
testPtr = strtok_s(source, "、", &w);
puts(testPtr);
testPtr = strtok_s(NULL, "、", &w);
puts(testPtr);
testPtr = strtok_s(NULL, "、", &w);
puts(testPtr);
return 0;
}
strcpy_s(test, strlen(source), source);
の第2引数にさっきまでなかった*strlen(source)*が増えています。これはコピーする文字列の大きさをバイト単位で指定しています。なぜなのかはまた後程。
主なファイル操作関数
こちらはfopen()
とfprintf()
とfscanf()
の3つを説明しようと思います。
# include <stdio.h>
# include <string.h>
int main(void)
{
char *string, *string2;
FILE *filePtr;
if ((filePtr = fopen("test.txt", "r")) == NULL)
return 0;
fscanf(filePtr, "%s,%s", string, string2);
fclose(filePtr);
if ((filePtr = fopen("test.txt", "w")) == NULL)
return 0;
fprintf(filePtr, "%s,%s", string2, string);
fclose(filePtr);
return 0;
}
ファイルに格納された文字列を","を区切りに入れ替えるプログラムです。これをVSでコンパイルするとまたしてもエラーが。理由は単純でファイル操作関数にもセキュリティ強化版があるからです。
# include <stdio.h>
# include <string.h>
int main(void)
{
char *string, *string2;
FILE *filePtr;
if (fopen_s(&filePtr, "test.txt", "r"))
return 0;
fscanf_s(filePtr, "%s,%s", string, 16, string2, 8);
fclose(filePtr);
if (fopen_s(&filePtr, "test.txt", "w"))
return 0;
fprintf_s(filePtr, "%s,%s", string2, string);
fclose(filePtr);
return 0;
}
まずfopen_s(&filePtr, "test.txt", "r")
で注意すべきは第1引数が&filePtr
であることです。つまりはポインタのポインタ(FILE型のダブルポインタ)を渡せということですね。
そしてfscanf_s(filePtr, "%s,%s", string, 16, string2, 8);
ではchar*型のstring,string2それぞれの後ろに16,8という謎の数値が現れました。これはstrcpy_s
関数の時と同じで格納する文字列のバイト数を示しています。しかし、なぜこのようなことが必要なのでしょうか?
文字列を格納するときにバイト数を指定する理由とは
まず、文字列とはなんだったのか思い出してみましょう。文字列とはchar型配列のことでしたよね。
こうしたメモリの浸食は時に深刻なエラーをもたらすことがあります。それを未然に防いでいるのが格納前のバイト数チェックです。容量が足りないのに無理やり動かして止まるよりもその時点で「ダメでした」って教えてくれるほうがいいじゃないってことです。
メモリの確保は逆順に行われるのでこの図も実は正確ではありませんが「後から宣言したんだし多少はみ出してもいっか☆」と安易に考えると痛い目にあいます。そうならないためにも必要な容量を確実にかつ無駄なく確保できるようにしたいですね。
DxLibを使う上で気を付けること
DxLibにはDxLib_End()
という関数がありDxLibを止めてくれています。これを忘れると裏でひたすら何かをし続けメモリを食いつぶす害虫へと早変わりしてしまいます。しかしこれには落とし穴があるんです。
処理が終わってプログラムを終了する前には自分で呼び出しますが***「×マーク」をユーザーがクリックしてプログラムを終了させるとDxLib_End()
は実行されないんです!このままでは害虫を作ることになってしまいます...。
~~そこでSetWindowUserCloseEnableFlag(FALSE);
***の出番です!これは「×マーク」や「Alt + F4」の動作を検知してねというものです。これを実行しておけば~~次のようなコードで完全終了が可能になります。
# include "DxLib.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
if (DxLib_Init() == -1)
return false;
while (!ProcessMessage()){
if ((CheckHitKey(KEY_INPUT_ESCAPE)) || (GetWindowUserCloseFlag())){ // Escまたは×ボタン、ALT+F4が押されたら終了
DxLib_End();
return 0;
}
}
return 0;
}
最後に
つたない文章と要領を得ないページを最後まで見ていただきありがとうございます。また思いついたこと等あれば投稿しますのでどうぞよろしくお願いします。