VS Community 2017でC++(DxLib)開発する際の注意点

#はじめに
　多くの人は「C言語で開発してるけど環境がVSだから.cppになっているだけだよね」と考えているかと思いますが、私たちがC言語の書き方に固執しているだけであって開発環境は紛れもなくC++なんです。とはいえ委縮することはありません。C++でもC言語プログラミングは問題なく可能ですから。

#VS開発の際の面倒なこと
　すごく地味ですが我々C言語民にとって非常に煩わしいのがファイル操作関数と文字列操作関数はセキュリティ強化版を使えというVSからの指令。警告というかたちで表示されなんとも鬱陶しい。
　例えばファイル操作関数の場合だと`#pragma warning( disable : 4996 )`をファイルの最初に記述すれば事足りるのですがこれはあまりお勧めしません。
　警告されるということは何らかの問題があるということなので警告レベルを最大にしたコンパイルで警告ゼロになるようなコーディングをしましょう。

#主な文字列操作関数
　今回は`strcpy()`と`strtok()`の説明をしようと思います。目先で使ったのがこれだっただけなので深い意味はありませんが他の関数も知りたいときはリクエストを送ってもらえれば対応するかもです。
***_s***はセキュリティ強化版であることを意味しています。VSではセキュリティ強化版でないとコンパイルエラーが出ます。


```C:sample.c
#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は許してはくれません。

```C++:sample.cpp
#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つを説明しようと思います。

```C:sample.c
#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でコンパイルするとまたしてもエラーが。理由は単純で***ファイル操作関数にもセキュリティ強化版がある***からです。

```C++:sample.cpp
#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型配列のことでしたよね。
![メモリ図.png](https://qiita-image-store.s3.amazonaws.com/0/312202/5c38f6d3-8efe-3109-e4a0-c7d3c6de9e4d.png)
　こうしたメモリの浸食は時に深刻なエラーをもたらすことがあります。それを未然に防いでいるのが格納前のバイト数チェックです。容量が足りないのに無理やり動かして止まるよりもその時点で「ダメでした」って教えてくれるほうがいいじゃないってことです。
　メモリの確保は逆順に行われるのでこの図も実は正確ではありませんが「後から宣言したんだし多少はみ出してもいっか☆」と安易に考えると痛い目にあいます。そうならないためにも必要な容量を確実にかつ無駄なく確保できるようにしたいですね。

#DxLibを使う上で気を付けること
　DxLibには`DxLib_End()`という関数がありDxLibを止めてくれています。これを忘れると裏でひたすら何かをし続け***メモリを食いつぶす害虫***へと早変わりしてしまいます。しかしこれには***落とし穴***があるんです。
　処理が終わってプログラムを終了する前には自分で呼び出しますが***「×マーク」をユーザーがクリック***してプログラムを終了させると`DxLib_End()`は***実行されない***んです！このままでは害虫を作ることになってしまいます...。
　~~そこで***`SetWindowUserCloseEnableFlag(FALSE);`***の出番です！これは「×マーク」や「Alt + F4」の動作を検知してねというものです。これを実行しておけば~~次のようなコードで完全終了が可能になります。

```C++:sample.cpp
#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;
}
```

#最後に
　つたない文章と要領を得ないページを最後まで見ていただきありがとうございます。また思いついたこと等あれば投稿しますのでどうぞよろしくお願いします。