今まで使用していた「scanf」は危険ってどういうこと?
scanfを使用するとエラーになる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include<stdio.h> #include <stdlib.h> // system("pause")を使用するため int main(void) { // 変数の初期化 int a; // 数値を入力する scanf("%d", &a); // 入力値を表示 printf("%dを入力しました\n", a); system("pause"); // これが一時停止の命令 return 0; } |
このソースをビルドしようとすると、Visual Studioではビルドエラーが出て、ビルドも実行もできない。
エラー C4996 ‘scanf’: This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
この関数または変数は危険です。 代わりにscanf_sの使用を検討してください。
セキュリティが強化されたscanf_s
Visual Studioがscanfを使うなというビルドエラーが出てしまうので、なぜか?
と思いググってみると、scanf関数はバッファオーバーエラーの危険性があるという意見と、scanf関数はバッファオーバーエラーが出ると言うやつのスキルはクソという意見。などなど、ほぼ宗教戦争になっていることもあり。
このブログで紹介できるほど納得のいく情報は得られませんでした。すみません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// scanf_sのサンプル #include<stdio.h> #include <stdlib.h> // system("pause")を使用するため int main(void) { // 変数の初期化 int a; char str[11]; // 数値を入力する printf(" 何か数値を入力してください\n "); scanf_s("%d", &a); // 入力値を表示 printf("%dを入力しました\n\n", a); // 文字列を10文字入力する printf(" 何か文字列を入力してください(10文字まで)\n "); scanf_s("%s", &str, _countof(str)); printf(" \"%s\"を入力しました\n",str); system("pause"); // これが一時停止の命令 return 0; } |
※_countof は、固定長配列の要素数を取得するためのマクロです。
char型配列を11個作っていますが、これはchar型配列[0]~[10]の11個を作って文字を10文字格納します。
一番最後のchar型変数には、文字列の終わりである「\0」が付加される。
入力させたい文字数+1個の配列を作ることです。
※10文字以上入力すると、変数に値は入りません。空のまま表示されます。
scanf_sの使い方
1 |
scanf_s(入力変換指定子, &変数名,変数の配列数) |
配列数は、「_countof()」マクロで取得しています。
入力変換指定子について
よく使う代表的な入力変換指定子については下記の通り
入力変換指定子 | 意味 |
%d | 10進整数 |
%u | 符号なし10進整数 |
%f | float型実数(小数) |
%lf | double型実数(小数) |
%i | 10進数、8進数、16進整数 |
%o | 8進数 |
%x | 16進整数 |
%s | 文字列 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
// scanf_sのサンプル #include<stdio.h> #include <stdlib.h> // system("pause")を使用するため int main(void) { // 変数の初期化 unsigned int a = 0; float f = 0.0; double d = 0.0; // 符号なし整数を入力する printf(" 符号なし10進整数を入力してください\n "); scanf_s("%u", &a); printf(" \"%u\"を入力しました\n\n",a); // 実数を入力する(float) printf(" 実数を入力してください(float)\n "); scanf_s("%f", &f); printf(" \"%f\"を入力しました\n\n", f); // 実数を入力する(double) printf(" 実数を入力してください(double)\n "); scanf_s("%lf", &d); printf(" \"%lf\"を入力しました\n\n", d); // 8進数を入力する printf(" 8進数を入力してください\n "); scanf_s("%o", &a); printf(" \"%o\"を入力しました\n\n", a); // 16進数を入力する printf(" 16進数を入力してください\n "); scanf_s("%x", &a); printf(" \"%x\"を入力しました\n\n", a); system("pause"); // これが一時停止の命令 return 0; } |
入力桁数を指定する
先ほど、文字型配列数-1以上の文字が入力されると変数の中に値が入らないということを書きましたが、そのような機能に頼るよりもこちらから入力桁数を決めて、その桁数以上の入力文字は切り捨てるというやり方のほうがいいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// scanf_sのサンプル #include<stdio.h> #include <stdlib.h> // system("pause")を使用するため int main(void) { // 変数の初期化 char str[11]; // 文字列を10文字入力する printf(" 何か文字列を入力してください(10文字まで)\n "); scanf_s("%5s", &str, _countof(str)); printf(" \"%s\"を入力しました\n",str); system("pause"); // これが一時停止の命令 return 0; } |
13行目のscanf_sの入力変換指定子の「%5s」で5文字までの入力を受け付けています。
代入抑止指定
代入抑止指定については、想像していたのと動作が異なっていたため。
ここでは取り上げません。
これは、入力変換指定子に「*」を付けるとそのデータははじかれるというもので
「%*s%d」なら、文字列をはじいて「hello123」なら「123」が変数に格納される
というモノらしいですが。
Visual Studioではうまくいかなかったため、割愛します。
入力したデータの変換については、また後日書きます。
コメント
この記事へのトラックバックはありません。
この記事へのコメントはありません。