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

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

ただいまの
回答率

87.91%

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

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,261

score 28

#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演算するのか、疑問です。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    退会済みユーザー

    2016/08/05 23:35

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

    キャンセル

  • cingyan

    2016/08/06 17:04

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

    キャンセル

  • Zuishin

    2018/02/13 11:11

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

    キャンセル

  • cingyan

    2018/02/13 12:13

    分かりました。

    キャンセル

回答 4

+1

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

#include <stdio.h>
#include <stdlib.h>

int main(){
  int a;
  scanf("%d", &a);
  printf("  %%d -> %d (%%d)\n", a);
  printf("  %%d -> %i (%%i)\n", a);

  scanf("%i", &a);
  printf("  %%i -> %d (%%d)\n", a);
  printf("  %%i -> %i (%%i)\n", a);

  int b,c;
  char str[] = "012.012.0x12.";
  sscanf(str, "%d.%i.%i.", &a, &b, &c);
  printf("\n");
  printf("%s -> a(%%d).b(%%i).c(%%i)\n", str);
  printf("   a = %d\n", a);
  printf("   b = %d\n", b);
  printf("   c = %d\n", c);

  char str2[] = "99.99.099.";
  sscanf(str2, "%d.%i.%i.", &a, &b, &c);
  printf("\n");
  printf("%s -> a(%%d).b(%%i).c(%%i)\n", str2);
  printf("   a = %d\n", a);
  printf("   b = %d\n", b);
  printf("   c = %d\n", c);

  return 0;
}

結果。
コメントに書いた通り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        # 接頭辞 00x がなければ 10進数として値を取得して変数に入っている。
   c = 0         # 099 は先頭が0なので8進と判断されたが0998進として不正なので

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/08/07 13:44

    ご回答、ありがとうございました。

    %i と %dのことについて詳しくお教えいただきたく思います。
    16進数と8進数ということですが、よく分かりません。

    http://wisdom.sakura.ne.jp/programming/c/c57.html
    ここに%iがありましたが、よく分かりませんでした。

    キャンセル

  • 2016/08/07 14:29

    「よく分かりませんでした。」ではこちらもわかりません。
    16進数と8進数について知らないのであればそれは自分で調べてください。

    printfにおいては%iと%dに結果的に違いはありません。どちらも数値を受け取り符号付整数で出力されるので同じになります。

    scanfにおいては %iは、入力が10進表記、8進表記、16進表記であってもそれにあった整数に変換されて変数に格納されます。

    これについては簡単なサンプルを追記しておきます。

    よく見たら、私の回答最後が切れているようでね。電波悪いところでやったからかな。申し訳ないです。それも補足しておきます。(切れたのは1行くらいですが)

    キャンセル

  • 2016/08/08 12:45

    大変詳しい回答ありがとうございました。
    よく分かりました。

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

    キャンセル

+1

8ビットの場合と16ビットの場合で、ANDマスクしていますが、意味が分かりません。
resultはすでに先の演算で値ができているのに、それをあえてAND演算するのか、疑問です。

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/14 12:55

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

    キャンセル

checkベストアンサー

0

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

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

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


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

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

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


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

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


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

そして、

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


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

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

最後に

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


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

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

長文失礼しました。

-- 追記 --
上記の

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


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

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


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


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

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

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


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

char a;

a = 'Q';
printf\(" %c",a \);


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

char a;
char list\[3\];

a = 'Q';
strcpy\(&list\[0\]," %c"\) ;  /\* listに書式指定文字列を入れる \*/

printf\(&list\[0\],a \);


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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/08/07 13:24

    ご回答、ありがとうございました。
    for文で、最後の '1':'0'が入ってから、インクリメントするので、次の文字列が入るアドレスにポインタがセットされたということですね。
    それで、次のstrcpyの文字列が入るようになるということですね。

    printf(&line[0],op[0]); は、op[0] が%cのための引数ということですね。

    大変、よく分かりました。ありがとうございました。

    キャンセル

  • 2016/08/08 12:48

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

    キャンセル

0

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

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

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/08/07 13:29

    ご回答、ありがとうございました。

    pに&line[0]のアドレスを入れているのは、分かっていましたが、それで、どうして実行結果のようになるのかが、分かりませんでした。

    %iと%dについては、私も調べましたが、具体的に何が違うか分かりませんでした。
    ありがとうございました。

    キャンセル

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

  • ただいまの回答率 87.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る