C言語でsprintf関数の使い方を教えて!
こういった悩みにお答えします.
本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Verse(UEFN), Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
目次
sprintf関数
1 |
int sprintf(char *str, const char *format, ...); |
sprintf関数は,formatに従って出力を文字列strに書き込む関数です.
※printf関数は標準出力に書き込きます.
sprintf関数の他の部分(書式や返り値)はprintf関数と同様です.
sprintf関数を利用するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #define BUFSIZE 64 int main(void) { int i = 123; double d = 3.14; char s[] = "abc"; char str[BUFSIZE]; sprintf(str, "i = %d, d = %lf, s = %s\n", i, d, s); printf("%s", str); return 0; } |
実行結果は以下になります.
複数の変数(int型,double型,char型の配列)を文字列に変換できていることがわかります.
1 2 3 |
$ gcc sprintf.c $ a.out i = 123, d = 3.140000, s = abc |
sprintf関数の注意点として,バッファサイズより長い文字をstrに書き込むとバッファオーバーフローが発生してしまいます.
sprintf.cのBUFSIZEを64から16に変更したコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #define BUFSIZE 16 int main(void) { int i = 123; double d = 3.14; char s[] = "abc"; char str[BUFSIZE]; sprintf(str, "i = %d, d = %lf, s = %s\n", i, d, s); printf("%s", str); return 0; } |
実行結果は以下になります.
コンパイル時に警告が発生し(2~11行目),実行時にバッファオーバーフロー「*** stack smashing detected ***: terminated」が発生していることがわかります(14行目).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ gcc sprintf2.c sprintf2.c: In function 'main': sprintf2.c:16:29: warning: '%lf' directive writing between 3 and 317 bytes into a region of size between 0 and 5 [-Wformat-overflow=] 16 | sprintf(str, "i = %d, d = %lf, s = %s\n", i, d, s); | ^~~ sprintf2.c:16:16: note: assuming directive output of 8 bytes 16 | sprintf(str, "i = %d, d = %lf, s = %s\n", i, d, s); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ sprintf2.c:16:3: note: 'sprintf' output between 22 and 349 bytes into a destination of size 16 16 | sprintf(str, "i = %d, d = %lf, s = %s\n", i, d, s); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $ a.out i = 123, d = 3.140000, s = abc *** stack smashing detected ***: terminated abort (core dumped) a.out |
snprintf関数
1 |
int snprintf(char *str, size_t size, const char *format, ...); |
snprintf関数は,最大でsizeバイトをstrに書き込む関数です.
sizeには文字列を終端する'\0'もを含まれます.
sprintf関数とは異なり,snprintf関数はバッファオーバーフローを回避することができます.
sprintf2.cのsprintf関数をsnprintf関数に変更したコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #define BUFSIZE 16 int main(void) { int i = 123; double d = 3.14; char s[] = "abc"; char str[BUFSIZE]; snprintf(str, BUFSIZE, "i = %d, d = %lf, s = %s\n", i, d, s); printf("%s", str); return 0; } |
実行結果は以下になります.
コンパイル時の警告は同様に発生しますが,実行時に「i = 123, d = 3.」と出力する文字数を'\0'文字を含めて16文字以内にすることで,バッファオーバーフローを回避できていることがわかります.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ gcc snprintf.c snprintf.c: In function 'main': snprintf.c:16:39: warning: '%lf' directive output may be truncated writing between 3 and 317 bytes into a region of size between 0 and 5 [-Wformat-truncation=] 16 | snprintf(str, BUFSIZE, "i = %d, d = %lf, s = %s\n", i, d, s); | ^~~ snprintf.c:16:26: note: assuming directive output of 8 bytes 16 | snprintf(str, BUFSIZE, "i = %d, d = %lf, s = %s\n", i, d, s); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ snprintf.c:16:3: note: 'snprintf' output between 22 and 349 bytes into a destination of size 16 16 | snprintf(str, BUFSIZE, "i = %d, d = %lf, s = %s\n", i, d, s); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $ a.out i = 123, d = 3. |
sprintf_s関数
1 |
int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...); |
sprintf_s関数は,sprintf関数にセキュリティ機能を追加したC11規格の関数です.
sprintf/snprintf関数とは異なり,sprintf_s関数はバッファオーバーフローが発生する場合,エラーコードを返して終了します.
これにより,バッファオーバーフローの発生を正常に検知することができます.
sprintf_s関数はVisual Studioでは利用できますが,GCC/Clangでは利用できないことに注意して下さい.
sprintf2.cのsprintf関数をsprintf_s関数に変更したコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #define BUFSIZE 16 int main(void) { int i = 123; double d = 3.14; char s[] = "abc"; char str[BUFSIZE]; sprintf_s(str, BUFSIZE, "i = %d, d = %lf, s = %s\n", i, d, s); printf("%s", str); return 0; } |
Visual Studioでの実行結果は以下になります.
バッファオーバーフローが発生し,コード3を返して終了していることがわかります.
1 2 |
*.exe (プロセス *) は、コード 3 で終 了しました。 このウィンドウを閉じるには、任意のキーを押してください... |
まとめ
C言語でsprintf関数と,派生関数のsnprintf/sprintf_s関数の使い方を紹介しました.
sprintf関数はバッファオーバーフローが発生してしまいますが,snprintf/sprintf_s関数はバッファオーバーフローを回避/検知できます.
また,sprintf_s関数はGCC/Clangでは利用できないこと,Visual Studioで利用できることを覚えておきましょう!
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!