質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

2404閲覧

C言語 dumpコマンド作成 渡された引数の判定が上手くいかない

kokok

総合スコア145

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2019/08/10 13:46

編集2019/08/19 02:07

c

1 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5#pragma warning(disable: 4996) 6 7#define H_PRT 0x02// ヘッダ印字オプション 8#define C_PRT 0x01// 文字印字オプション 9#define ROW 16 // 1行に表示する文字 10#define TEXTBUF 16 //テキストファイル用のバッファ 11#define FILE_ERR 10// 12#define OPT_ERR 10 13 14 15//オプション 16typedef struct { 17 18 char* infilename; 19 char prt_charflag; 20 char prt_headerflag; 21 char prt_help; 22 int offsetval; 23 int filecheck; 24 int optcheck; 25} opts_t; 26 27//プロトタイプ宣言 28void dump(unsigned char* staddr, int dsize, opts_t* opts); 29 30//オプションの解析 31int opts_analisys(int argc, char* argv[], opts_t* opts) { 32 33 int cnt; 34 int err = 0; 35 for (cnt = 1; cnt < argc; cnt++) { //引数の数ループ 36 if (*argv[cnt] == '/') { //引数の最初の文字が/なら 37 38 switch (*(argv[cnt] + 1)) { 39 40 case'H': 41 case'h': 42 43 opts->prt_headerflag = 0; //16行毎にヘッダーの出力 prt_headerflag; = 0 44 45 break; 46 case 'c': 47 case 'C': 48 49 opts->prt_charflag = 1; //ダンプのあとに文字を表示(デフォルトは= 1 prt_charflag; = 1 50 51 break; 52 case 'o': 53 case 'O': 54 opts->offsetval = 1; 55 56 break; 57 58 case '?': 59 opts->prt_help = 1; 60 break; 61 62 default: //オプションが無いとき、エラー 63 64 opts->optcheck = OPT_ERR; 65 err = OPT_ERR; //opt err 66 67 break; 68 } 69 } 70 71 if (opts->infilename == NULL) { 72 opts->infilename = argv[cnt]; //ファイル名をopts.infilename へ 73 } 74 else { 75 76 opts->filecheck = FILE_ERR; 77 err = FILE_ERR; 78 79 return err; 80 } 81 } 82 83 return err; 84} 85 86int main(int argc, char* argv[]) { 87 88 89 char bin_data[TEXTBUF]; // テキストファイル読み込み用 90 int readnum; 91 opts_t opts; 92 int result =0; 93 94 //構造体の初期化 95 opts.infilename ; 96 opts.prt_charflag ; 97 opts.prt_headerflag; 98 opts.prt_help ; 99 opts.offsetval ; 100 opts.filecheck = 0; 101 opts.optcheck = 0; 102 103 FILE* file; 104 105 result = opts_analisys(argc, argv, &opts); //オプションの解析 106 107 //if(ファイルエラー   または  オプションで?) 108 if (((result && opts.filecheck) != 0)||((result && opts.optcheck)!= 0) || (opts.prt_help == 1)) { 109 110 if ((result & opts.filecheck) != 0) { 111 fprintf(stderr, "ファイル指定が2つあります"); 112 } 113 if ((result & opts.optcheck) != 0) { 114 115 fprintf(stderr, "オプション指定が間違っています"); 116 } 117 if (opts.prt_help == 1) { 118 fprintf(stderr, "構文: mydump[<opts>] [<inpath>] [<opts>]\n機能: ファイルやデバイスの内容を16進で表示する\nオプション:\n/ o[=]<hex> オフセットアドレスの指定(省略の場合は0000)\n/ h 16行毎にヘッダーの出力(デフォルトは = 0)\n / c   ダンプのあとに文字を表示(デフォルトは = 1)\n / ?   使用方法の表示(このメッセージを表示)"); 119 } 120 return; 121 } 122 123 if (opts.infilename != NULL) { 124 125 //fseek(*file, opts.offsetval, SEEK_CUR); 126 127 file = fopen(opts.infilename, "rb"); 128 fseek(file, opts.offsetval, SEEK_END); 129 } 130 else { 131 file = stdin; 132 return; 133 } 134 135 while (readnum = fread(bin_data, sizeof(unsigned char), TEXTBUF, file) > 0) { 136 137 dump(bin_data, readnum, &opts); 138 } 139 140 if (opts.infilename != NULL){ 141 fclose(file); //ファイルが開かれたらクローズ 142 } 143} 144//印刷 145void dump(unsigned char* staddr, int dsize, opts_t *opts) { 146 147 int startcnt = 0;// ダンプするバイト数のカウント 148 int bytecnt; // 16回ループさせる 149 int savecnt; 150 static int address = 0; //アドレスの表示 151 static int onhold = 0; //文字出力が最後のバイトだった時の一時保留 152 153 static int headercnt = 0; 154 155 char addrhead[] = "Addr"; 156 char hexa[] = "0 1 2 3 4 5 6 7 8 9 A B C D E F"; 157 char charprint[] = "0 2 4 6 8 A C E"; 158 159 while (startcnt < dsize) { 160 161 if (onhold != 0) { //前回最後が漢字だった場合、漢字を出力して改行 162 163 printf("%c%c\n", onhold, staddr[startcnt]); 164 } 165 166 //初回ヘッダー表示 opt & H_PRT == 0だった時は16行毎にヘッダー表示 167 if ((headercnt == 0) || ((opts->prt_headerflag & H_PRT) == 0) && (headercnt % ROW == 0)) { 168 //16行毎にヘッダー 169 170 printf(" %s %s ", addrhead, hexa); 171 172 173 if ((opts->prt_charflag & C_PRT) != 0) { //opt が文字表示の場合 文字ヘッダー表示 174 175 printf("%s", charprint); 176 } 177 178 printf("\n"); 179 printf("-------- ---- ---- ---- ---- ---- ---- ---- ----"); 180 181 if ((opts->prt_charflag & C_PRT) != 0) { ////opt が文字表示の場合 文字罫線表示 182 printf(" ----------------"); 183 184 } 185 printf("\n"); 186 } 187 188 headercnt++; 189 190 printf("%08x ", address); 191 192 savecnt = startcnt; 193 for (bytecnt = 0; bytecnt < ROW; bytecnt++) { 194 195 if (startcnt < dsize) { 196 printf("%02x", staddr[startcnt]); //16進数で出力 197 } 198 else { 199 printf(" "); 200 } 201 if (startcnt % 2 != 0) { 202 printf(" "); 203 } 204 startcnt++; 205 } 206 207 //文字の出力 208 209 if ((opts->prt_charflag & C_PRT) != 0) { //optで文字表示の場合 表示 210 211 startcnt = savecnt; 212 printf(" "); 213 214 for (bytecnt = 0; bytecnt < ROW; bytecnt++) { 215 216 if (onhold != 0) { //前回最後が漢字だった場合 次の行の最初にスペースと初期化 217 218 printf(" "); 219 onhold = 0; 220 bytecnt++; 221 startcnt++; 222 } 223 224 if (startcnt < dsize) { 225 226 //漢字だった場合 227 if ((staddr[startcnt] >= 0x81 && staddr[startcnt] < 0xa0) || (staddr[startcnt] >= 0xe0 && staddr[startcnt] < 0xfd)) { 228 229 if (bytecnt < ROW - 1) { //最後のバイトでなければ 230 231 printf("%c%c", staddr[startcnt], staddr[startcnt + 1]); 232 startcnt++; 233 bytecnt++; 234 } 235 else {//漢字で最後のバイトの場合、保留 236 237 onhold = staddr[startcnt]; 238 } 239 } 240 else { 241 242 if ((staddr[startcnt] >= 0x20 && staddr[startcnt] < 0x7f) || (staddr[startcnt] >= 0xa0 && staddr[startcnt] < 0xe0)) { 243 244 printf("%c", staddr[startcnt]); 245 } 246 else { 247 printf(""); 248 } 249 } 250 } 251 else { 252 printf(" "); 253 } 254 255 startcnt++; 256 } 257 if (onhold == 0) { //最後が漢字でなければ改行 258 printf("\n"); 259 } 260 } 261 else { //optが文字表示ではない場合 改行 262 263 printf("\n"); 264 } 265 address += ROW; //addressを出力しただけ足す 266 } 267} 268

引数でファイル名や オプションで /h /c などを渡しても

ファイル指定が2つあります" となってしまいます。

/? を渡してあげると /?の内容が表示されるので

下記のコードに問題がありそうなのですが、分からなかったのでアドバイス頂けると助かります。

c

1 //if(ファイルエラー   または  オプションで?) 2 if (((result && opts.filecheck) != 0)||((result && opts.optcheck)!= 0) || (opts.prt_help == 1)) { 3 4 if ((result & opts.filecheck) != 0) { 5 fprintf(stderr, "ファイル指定が2つあります"); 6 } 7 if ((result & opts.optcheck) != 0) { 8 9 fprintf(stderr, "オプション指定が間違っています"); 10 } 11 if (opts.prt_help == 1) { 12 fprintf(stderr, "構文: mydump[<opts>] [<inpath>] [<opts>]\n機能: ファイルやデバイスの内容を16進で表示する\nオプション:\n/ o[=]<hex> オフセットアドレスの指定(省略の場合は0000)\n/ h 16行毎にヘッダーの出力(デフォルトは = 0)\n / c   ダンプのあとに文字を表示(デフォルトは = 1)\n / ?   使用方法の表示(このメッセージを表示)"); 13 } 14 return; 15 } 16

----追記----

コマンドラインで実行すると
Debug Assertion Failed

Expression: stram != nullptr

とエラーが出てしまいます。

c

1if (opts.infilename != NULL) { 2 3 file = fopen(opts.infilename, "rb"); 4 // fseek(file, opts.offsetval, SEEK_END); 5 6 7 } 8 else { 9 file = stdin; 10 return 0; 11 } 12 13 while ((readnum = fread(bin_data, sizeof(unsigned char), TEXTBUF, file)) > 0) { 14 15 dump(bin_data, readnum, &opts); 16 } 17 18 if (opts.infilename != NULL) { 19 fclose(file); //ファイルが開かれたらクローズ 20 } 21}

ここに問題があるとある気がするのですが
エラー内容があまり理解できなかったのでアドバイス頂けると助かります。

----追記2----

c

1 if (opts.infilename != NULL) { //コマンドラインからファイルの入力があれば 2 3 4 if ((file = fopen(opts.infilename, "rb")) != NULL) { //ファイルがあれば 5 6 fseek(file, opts.offsetval, SEEK_END); 7 } 8 else { //ファイルがなければ 9 //printf( "ファイルがありません"); 10 return 0; 11 } 12 } 13 else { 14 file = stdin; 15 } 16 17 while ((readnum = fread(bin_data, sizeof(unsigned char), TEXTBUF, file)) > 0) { 18 19 dump(bin_data, readnum, &opts); 20 } 21 22 if (opts.infilename != NULL) { 23 fclose(file); //ファイルが開かれたらクローズ 24 } 25}

このように変更したところエラーメッセージは出なくなりましたが、ファイルの中身が表示されません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

下記のコードに問題がありそうなのですが

その部分も十分怪しい臭いがしますが、もうひとつ怪しいのは opts_analisys() 関数です。オプションを正しく解析できていないのではありませんか。

opts_analisys() 関数が返す情報は2種類あります。

  • 関数の戻り値。即ち result 変数に返る値
  • opts 構造体変数にセットする値。即ち infilename 〜 optcheck メンバの値。

これらの値を質問者自身で確認することをお勧めします。プログラムを思い通りに動かしたかったらデバッグ作業は不可欠です。他人に頼っていたら思い通りになりません。デバッガが使えるに越したことはありませんが、このケースは所謂printfデバッグが十分役に立ちそうです。
例えば opts 構造体の内容を表示する関数として

C

1void printOpts(opts_t *opts) 2{ 3 printf("infilename = %p\n", opts->infilename); 4 printf("prt_charflag = %d\n", opts->prt_charflag); 5 printf("prt_headerflag = %d\n", opts->prt_headerflag); 6 printf("prt_help = %d\n", opts->prt_help); 7 printf("offsetval = %d\n", opts->offsetval); 8 printf("filecheck = %d\n", opts->filecheck); 9 printf("optcheck = %d\n", opts->optcheck); 10 printf("\n"); 11}

を追加して、opts_analisys() 関数呼出しの前後を

C

1 printOpts(&opts); // デバッグ表示 2 result = opts_analisys(argc, argv, &opts); // オプション解析 3 printf("result = %d\n", result); // デバッグ表示 4 printOpts(&opts); // デバッグ表示

とすれば、result と opts 変数の値を確認できます。printOpts() は opts_analisys() の中からも呼び出せます。
Enjoy !

投稿2019/08/11 11:07

rubato6809

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kokok

2019/08/19 01:03

ありがとうございます。 変数の値を一つずつ確認してみます。
guest

0

とりあえず、気が付いた箇所のみコメントしておきます。
//追加
//変更
のコメントをいれておきます。

C

1//オプションの解析 2int opts_analisys(int argc, char* argv[], opts_t* opts) { 3 4 int cnt; 5 int err = 0; 6 for (cnt = 1; cnt < argc; cnt++) { //引数の数ループ 7 if (*argv[cnt] == '/') { //引数の最初の文字が/なら 8 9 switch (*(argv[cnt] + 1)) { 10 11 case'H': 12 case'h': 13 14 opts->prt_headerflag = 1; //変更 //16行毎にヘッダーの出力 prt_headerflag; = 0 15 16 break; 17 case 'c': 18 case 'C': 19 20 opts->prt_charflag = 1; //ダンプのあとに文字を表示(デフォルトは= 1 prt_charflag; = 1 21 22 break; 23 case 'o': 24 case 'O': 25 opts->offsetval = 1; 26 27 break; 28 29 case '?': 30 opts->prt_help = 1; 31 break; 32 33 default: //オプションが無いとき、エラー 34 35 opts->optcheck = OPT_ERR; 36 err = OPT_ERR; //opt err 37 38 break; 39 } 40 continue; //追加 41 } 42 43 if (opts->infilename == NULL) { 44 opts->infilename = argv[cnt]; //ファイル名をopts.infilename へ 45 } 46 else { 47 48 opts->filecheck = FILE_ERR; 49 err = FILE_ERR; 50 51 return err; 52 } 53 } 54 55 return err; 56} 57 58int main(int argc, char* argv[]) { 59 60 61 char bin_data[TEXTBUF]; // テキストファイル読み込み用 62 int readnum; 63 opts_t opts; 64 int result =0; 65 66 //構造体の初期化 67 opts.infilename = NULL; //追加 68 opts.prt_charflag = 0; //追加 69 opts.prt_headerflag = 0; //追加 70 opts.prt_help = 0; //追加 71 opts.offsetval = 0; //追加 72 opts.filecheck = 0; 73 opts.optcheck = 0; 74 75 FILE* file; 76 77 result = opts_analisys(argc, argv, &opts); //オプションの解析 78 79 //if(ファイルエラー   または  オプションで?) 80 if (((result && opts.filecheck) != 0)||((result && opts.optcheck)!= 0) || (opts.prt_help == 1)) { 81 82 if ((result & opts.filecheck) != 0) { 83 fprintf(stderr, "ファイル指定が2つあります"); 84 } 85 if ((result & opts.optcheck) != 0) { 86 87 fprintf(stderr, "オプション指定が間違っています"); 88 } 89 if (opts.prt_help == 1) { 90 fprintf(stderr, "構文: mydump[<opts>] [<inpath>] [<opts>]\n機能: ファイルやデバイスの内容を16進で表示する\nオプション:\n/ o[=]<hex> オフセットアドレスの指定(省略の場合は0000)\n/ h 16行毎にヘッダーの出力(デフォルトは = 0)\n / c   ダンプのあとに文字を表示(デフォルトは = 1)\n / ?   使用方法の表示(このメッセージを表示)"); 91 } 92 return 0; 93 } 94 95 if (opts.infilename != NULL) { 96 97 //fseek(*file, opts.offsetval, SEEK_CUR); 98 99 file = fopen(opts.infilename, "rb"); 100 fseek(file, opts.offsetval, SEEK_END); 101 } 102 else { 103 file = stdin; 104 return 0; 105 } 106 107 while (readnum = fread(bin_data, sizeof(unsigned char), TEXTBUF, file) > 0) { 108 109 dump(bin_data, readnum, &opts); 110 } 111 112 if (opts.infilename != NULL){ 113 fclose(file); //ファイルが開かれたらクローズ 114 } 115 return 0; 116}

mainの最初の部分

C

1int main(int argc, char* argv[]) { 2 3 4 char bin_data[TEXTBUF]; // テキストファイル読み込み用 5 int readnum; 6 opts_t opts; 7 int result =0; 8 9 //構造体の初期化 10 opts.infilename = NULL; //追加 11 opts.prt_charflag = 0; //追加 12 opts.prt_headerflag = 0; //追加 13 opts.prt_help = 0; //追加 14 opts.offsetval = 0; //追加 15 opts.filecheck = 0; 16 opts.optcheck = 0; 17 18 FILE* file; 19

投稿2019/08/11 05:16

tatsu99

総合スコア5438

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

if (*argv[cnt] == '/') {

この if ()を抜けた後、どうなっているでしょうか?

ファイル指定が2つあります

との事なので、エラーフラグ立てる時に、そのファイル名を出力してみましょう。

なお、"&" "&&" が混在してますが、使い分けましょう。(論理積はむ、&&)
opts.offsetval 初期化されてないみたいですが、、他は?

細かく、質問立ててますが、デバッガとは言わなくても、途中経過を出力するとかで、ある程度分かりませんか?

投稿2019/08/10 14:38

編集2019/08/10 14:40
pepperleaf

総合スコア6383

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問