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

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

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

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

Q&A

解決済

4回答

1863閲覧

バッファを使用した画面表示

cingyan

総合スコア29

C

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

0グッド

0クリップ

投稿2016/08/05 07:03

編集2018/02/11 03:07
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_BYTE 255 /*8ビットで表現できる最大値*/ enum OP{ AND = '&',OR = '|' ,XOR = '^',NOT = '~'}; int main(void) { unsigned char line[256],op[2]; /*入出力バッファ・演算子格納*/ unsigned char *p; /*出力バッファワークポインタ*/ unsigned d1,d2,result; /*被演算数・結果*/ unsigned mask=0x0001; /*マスクパターン*/ int ret,shift,save; /*戻り値セーブ・ビットカウンタ*/ for(;;) /*無限ループ*/ { op[0]='\0',p=&line[0]; /*変数初期化*/ printf("\n> "); /*プロンプト表示*/ gets(&line[0]); /*1行入力*/ ret=sscanf(&line[0],"%1s%i",op,&d2); /*単項演算で読み込む*/ if((ret==2)&&(op[0]==NOT)) { result = ~d2; /*否定演算実行*/ shift=save=(d2>MAX_BYTE)? 16:8; /* 16/18ビット幅を決定*/ } else if((op[0]=='q') || (op[0]=='Q')) /*終了か?*/ { exit(EXIT_SUCCESS); } else { /*** 2項演算として読み込み直す***/ if(sscanf(&line[0],"%i%1s%i",&d1,op,&d2) != 3) { continue; /*入力不正*/ } switch(op[0]) { case AND: result=d1&d2; break; /*論理積*/ case OR: result=d1|d2; break; /*論理和*/ case XOR: result=d1^d2; break; /*排他的論理和*/ default: continue; /*入力誤り*/ } shift=save=((d1>MAX_BYTE) || (d2>MAX_BYTE))?16:8;/*ビット幅決定*/ strcpy(p,"\n\t "); /***第1項を文字列で2進化し、バッファに格納する***/ for(p=&line[strlen(&line[0])];shift>0;shift--) { *p++ = (d1 & (mask<<(shift-1)))? '1':'0'; } } result &=(save==8)? 0x00ff:0xffff; printf("---> %X(hex) / %u(dec)",result,result); /*答えの表示*/ strcpy(p,"\n\t%c)"); /*演算子設定用出力*/ /***第2項(否定では第1項)を文字列で2進化し、バッファに格納する***/ for(shift=save,p=&line[strlen(&line[0])];shift>0;shift--) { *p++ = (d2 & (mask<<shift-1))? '1':'0'; } strcpy(p,"\n\t "); /***線を引く***/ for(shift=save,p=&line[strlen(&line[0])];shift>=0;shift--) { *p++ = '-'; } strcpy(p,"\n\t "); /***演算結果を文字列で2進数化し、バッファに格納する***/ for(shift=save,p=&line[strlen(&line[0])];shift>0;shift--) { *p++ = (result & (mask<<shift-1))? '1':'0'; } strcpy(p,"\n"); /*バッファ設定完了*/ printf(&line[0],op[0]); /*バッファを書き出す*/ } return EXIT_SUCCESS; }

C言語によるプログラミング 応用編という本で、ビットごとの論理演算の2進数演算形式表示というプログラムから質問です。

strcpy(p, や *p++=
として文字列をpにコピーや代入をしていますが、どうしてそれが最後のprintfで表示になるのでしょうか。
この操作をする度にpの値が変わるのではないかと思います。
表示させるにはその都度、printfをしなければならないのかと思っています。

もう一つお伺いします。
if(sscanf(&line[0],"%i%1s%i",&d1,op,&d2) != 3)
この %i というのは %d と同じと解釈して良いのでしょうか。
表記が違うので、何か異なる意味があるのでしたら教えてください。


2016/08/06 追記 実行結果

$ ./a.out > 3&10 ---> 2(hex) / 2(dec) 00000011 &)00001010 --------- 00000010 > 3|10 ---> B(hex) / 11(dec) 00000011 |)00001010 --------- 00001011 > 3^10 ---> 9(hex) / 9(dec) 00000011 ^)00001010 --------- 00001001 > ~3 ---> FC(hex) / 252(dec) ~)00000011 --------- 11111100 > q $

2016/08/06 追記
もう一つ、質問がありました。
if-else文が終わってから、

strcpy(p,"\n\t%c)"); //疑問点

この文で%cとあり、今回のプログラムでは、& | ^ ~ のどれかが表示されるのですが、
例えば、

int a=10; printf("%d",a);

のように、変換指定と、それに対する引数が必要だと思うのですが、上の疑問点には、引数が無いのになぜ、表示されるのでしょうか。


以上は分かりましたが、新たに疑問がありますので、ご回答よろしくお願いします。

2018/02/06 追記
新たな疑問が出たので、ご回答ください。

result &=(save==8)? 0x00ff:0xffff;

この文は無くても動きましたが、どのような意味があるのでしょうか。
8ビットの場合と16ビットの場合で、ANDマスクしていますが、意味が分かりません。
resultはすでに先の演算で値ができているのに、それをあえてAND演算するのか、疑問です。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2016/08/05 14:35

このソースって動作しているものですよね? また、動作しているものであれば、入力例と出力例を示してもらえると助かります。
cingyan

2016/08/06 08:04

実行結果を追記しました。よろしくお願いします。
Zuishin

2018/02/13 02:11

このサイトの使い方を根本的に間違えていると思います。前回の最も良い回答をベストアンサーにし、新しい質問を作ってください。
cingyan

2018/02/13 03:13

分かりました。
guest

回答4

0

8ビットの場合と16ビットの場合で、ANDマスクしていますが、意味が分かりません。

resultはすでに先の演算で値ができているのに、それをあえてAND演算するのか、疑問です。

否定(NOT)演算を含む場合、結果が8/16bitを超えて表現されるからですね。

result &=(save==8)? 0x00ff:0xffff;

が無い状態で~3を入力すると、結果が FC(hex) ではなく FFFFFFFC(hex) となるはずです。

投稿2018/02/13 03:20

HARQ

総合スコア181

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

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

cingyan

2018/02/14 03:55

ご回答、ありがとうございました。。 そういうことだったのですね。 ソースコードをちゃんと読んでいれば分かったと思うと、お恥ずかしいです。
guest

0

strcpy(p, や *p++=

この操作をする度にpの値が変わるのではないかと思います。

pはポインタであり、ポインタpがこのプログラムで司っているのは line配列上の位置です。
初期化時に lineの先頭(line[0])にセットされ、その後の操作(pに対する変更)でline上のどこを見ているかが変わります。

内容として変更されているのはlineになります。
pに対する変更(for文で行なわれている)はline上のどこを *pでみたいかの場所移動になります。
*p に対する変更は、 line上の実際の値の変更になります。

変更は全てlineに対して行われているので、最後にlineを出力すれば問題ありません。

この %i というのは %d と同じと解釈して良いのでしょうか。

だいたい同じですが、 '0x??'とか '0??' を指定した場合に%iは16進数や8進数として受け付けます。


追記

scanfの %i についてサンプルを書いておきます。
scanfで %i が 入力をどう整数かするかの参考にしてください。

scan_sample.c

c

1#include <stdio.h> 2#include <stdlib.h> 3 4int main(){ 5 int a; 6 scanf("%d", &a); 7 printf(" %%d -> %d (%%d)\n", a); 8 printf(" %%d -> %i (%%i)\n", a); 9 10 scanf("%i", &a); 11 printf(" %%i -> %d (%%d)\n", a); 12 printf(" %%i -> %i (%%i)\n", a); 13 14 int b,c; 15 char str[] = "012.012.0x12."; 16 sscanf(str, "%d.%i.%i.", &a, &b, &c); 17 printf("\n"); 18 printf("%s -> a(%%d).b(%%i).c(%%i)\n", str); 19 printf(" a = %d\n", a); 20 printf(" b = %d\n", b); 21 printf(" c = %d\n", c); 22 23 char str2[] = "99.99.099."; 24 sscanf(str2, "%d.%i.%i.", &a, &b, &c); 25 printf("\n"); 26 printf("%s -> a(%%d).b(%%i).c(%%i)\n", str2); 27 printf(" a = %d\n", a); 28 printf(" b = %d\n", b); 29 printf(" c = %d\n", c); 30 31 return 0; 32}

結果。
コメントに書いた通りprintf では %d%i も差はありません。
2回目の入力で099など 8進数でありえないものを入力した場合は0になります。99は10進として判断されるので問題ありません。

$ gcc scan_sample.c && ./a.out 077 %d -> 77 (%d) # 077 を %d で受け付け、 %dで出力。77と解釈されている %d -> 77 (%i) 077 %i -> 63 (%d) # 077 を %i で受け付け、 %dで出力。63 (8進数で077)と解釈されている %i -> 63 (%i) 012.012.0x12. -> a(%d).b(%i).c(%i) # 左の文字列を、右の「変数(書式文字)」ので受け付けた場合 a = 12 # 012 10進 b = 10 # 012 8進 c = 18 # 0x12 16進 99.99.099. -> a(%d).b(%i).c(%i) a = 99 b = 99 # 接頭辞 0 や 0x がなければ 10進数として値を取得して変数に入っている。 c = 0 # 099 は先頭が0なので8進と判断されたが099は8進として不正なので

投稿2016/08/05 07:28

編集2016/08/07 05:45
flied_onion

総合スコア2604

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

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

cingyan

2016/08/07 04:44

ご回答、ありがとうございました。 %i と %dのことについて詳しくお教えいただきたく思います。 16進数と8進数ということですが、よく分かりません。 http://wisdom.sakura.ne.jp/programming/c/c57.html ここに%iがありましたが、よく分かりませんでした。
flied_onion

2016/08/07 05:29

「よく分かりませんでした。」ではこちらもわかりません。 16進数と8進数について知らないのであればそれは自分で調べてください。 printfにおいては%iと%dに結果的に違いはありません。どちらも数値を受け取り符号付整数で出力されるので同じになります。 scanfにおいては %iは、入力が10進表記、8進表記、16進表記であってもそれにあった整数に変換されて変数に格納されます。 これについては簡単なサンプルを追記しておきます。 よく見たら、私の回答最後が切れているようでね。電波悪いところでやったからかな。申し訳ないです。それも補足しておきます。(切れたのは1行くらいですが)
cingyan

2016/08/08 03:45

大変詳しい回答ありがとうございました。 よく分かりました。 貴殿の最初の回答にあった、 >だいたい同じですが、 '0x??'とか '0??' を指定した場合に%iは16進数や8進数として受け付けます。 と言うことですね。 ありがとうございました。。
guest

0

ベストアンサー

御回答いただき、ありがとうございます。やっと自分の考えがつながりました。

このプログラムではline[]配列を最終的に表示に使うための長文列として扱っています。
まず初期化で

c

1 op[0]='\0',p=&line[0]; /*変数初期化*/

としており、pはline配列の先頭をまず示します。
以下2項演算だったときを例にして話を進めます。

この後、論理演算を終えた後に

c

1 strcpy(p,"\n\t ");

とし、lineの先頭にこの「\n\t \0」が格納されます。このヌル文字がポイントです。
次にfor文で

c

1for(p=&line[strlen(&line[0])];shift>0;shift--) 2{ 3 *p++ = (d1 & (mask<<(shift-1)))? '1':'0'; 4}

pにポインタが代入されています。しかしこれはstrlenが文字列長を返す関数であることを
考えると、先ほどの「\n\t \0」の長さを返すことになります。(ここではこの結果、pには
最後のヌル文字を指すアドレスが入っています。このヌル文字の先頭から「1」「0」で
2進数を並べていっているわけです。

そして、

C

1printf("---> %X(hex) / %u(dec)",result,result); /*答えの表示*/ 2strcpy(p,"\n\t%c)"); /*演算子設定用出力*/

ポインタpはループ内で1ずつ増えているので今は8桁の2進数の末尾の次のアドレスが入っています。
(これは「p++」を使っているためです)
そこにstrcpyで「\n\t%c)」を追加します。(ここは%cにあたる文字がありませんが、opですよね?)

というように、ポインタpは常にline配列に格納されている表示データの末尾を示すものとして利用されているわけです。エスケープ文字を駆使して改行したり、タブを入れたりしていますね。

最後に

C

1strcpy(p,"\n"); /*バッファ設定完了*/ 2printf(&line[0],op[0]); /*バッファを書き出す*/

strcpyで表示を全て完成させ、ずらずらと表示文が並んでいるline配列の先頭から表示してやることで、一気に結果を書き出していくという動作をしているわけです。
これが、1つめの答えです。(わかりにくかったらお知らせ下さい)

2つめの答えは、%iも%dも同じです。

長文失礼しました。

-- 追記 --
上記の

C

1strcpy(p,"\n\t%c)"); /*演算子設定用出力*/

は、やはり謎だったんですね(^^ゞ
私も初めて見るケースだったんでわかりませんが、推測では最後のprintf

C

1printf(&line[0],op[0]); /*バッファを書き出す*/

この第一項に結果の文字列(「%c」も含む)があり、そこに「op[0]」を入れるからだと思います。


追記で失礼します。私の回答が新しい疑問の解決につながっています。

上記でがあるから&|^~が表示されるというので合っています。

C

1printf(&line[0],op[0]); /*バッファを書き出す*/

があるから&|^~が表示されるというので合っています。
例えば

C

1char a; 2 3a = 'Q'; 4printf(" %c",a );

と書くと、スペース,'Q'の2文字が表示されることがわかると思います。
これを、

C

1char a; 2char list[3]; 3 4a = 'Q'; 5strcpy(&list[0]," %c") ; /* listに書式指定文字列を入れる */ 6 7printf(&list[0],a );

とやると、さきほどと同じことになります。
printfの第一引数は文字列のポインタなのでこういうことができるわけです。

投稿2016/08/06 09:15

編集2016/08/07 05:10
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

cingyan

2016/08/07 04:24

ご回答、ありがとうございました。 for文で、最後の '1':'0'が入ってから、インクリメントするので、次の文字列が入るアドレスにポインタがセットされたということですね。 それで、次のstrcpyの文字列が入るようになるということですね。 printf(&line[0],op[0]); は、op[0] が%cのための引数ということですね。 大変、よく分かりました。ありがとうございました。
cingyan

2016/08/08 03:48

改めて、ご回答ありがとうございました。 よく分かりました。
guest

0

として文字列をpにコピーや代入をしていますが、どうしてそれが最後のprintfで表示になるのでしょうか。

最初に変数pにline配列の先頭アドレスを設定しているからですね。

c

1op[0]='\0',p=&line[0];

pに値を設定することはline配列に値を設定することと同じことになります。

この %i というのは %d と同じと解釈して良いのでしょうか。

同じ解釈で問題ありません(私自身使ったことはありませんが・・・)
わからないときはまずは調べましょう。
書式を解説してるページなんて腐るほどでてきます。
http://www.mm2d.net/main/legacy/c/c-01.html

投稿2016/08/05 07:16

ttyp03

総合スコア16998

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

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

cingyan

2016/08/07 04:29

ご回答、ありがとうございました。 pに&line[0]のアドレスを入れているのは、分かっていましたが、それで、どうして実行結果のようになるのかが、分かりませんでした。 %iと%dについては、私も調べましたが、具体的に何が違うか分かりませんでした。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問