• ベストアンサー

6バイトを1フレームとして4つまで格納するキューでは25バイトキューは必要?

いま、PICマイコンの12F683のためにこれを作っているのですが、使い方としては6バイトのデータを1つのフレームとして受信できたら、それをキューに入れていこうと思っているのですが、最大4フレームまでストックできるようにしたいと思っていて、いっぱいになったらUARTで1フレームずつ出力するつもりです。 6バイトを4フレームなのでMAX_BUF_NUMは”24”に設定したのですが、これをやると今のプログラムは4フレーム目を格納しようとした時に、最後のバイトが格納できなくなってしまうことがわかり”25”としているのですが、24 + 1としなければいけないのでしょうか? int CountQueue() { if(Head <= Tail) return(Tail - Head); else return((MAX_BUF_NUM - 1) - Head + Tail); } void enqueue(BYTE x) { Tail=(Tail+1)%MAX_BUF_NUM; /*添え字の循環*/ if(Tail==Head){ QueueState = QSTATE_FULL; return; } UartSendBuf[Tail]=x; if(CountQueue()==(MAX_BUF_NUM-1)){ QueueState = QSTATE_FULL; } return; } BYTE dequeue(void) { BYTE x; if(Head==Tail){ QueueState = QSTATE_EMPTY; return; } Head=(Head+1)%MAX_BUF_NUM; x=UartSendBuf[Head]; UartSendBuf[Head]=0x00; //出力し終わったら0x00でクリアしておく。 return x; }

質問者が選んだベストアンサー

  • ベストアンサー
  • 6yemon
  • ベストアンサー率69% (25/36)
回答No.4

CountQueue()が、格納してるデータの個数をHeadとTailから計算しようとしてますね。これが本質的な原因のように思えます。 大きさが24の配列を使いきろうとすれば、データが皆無である場合も、データが満杯である場合も、どちらの場合も Head == Tail です。そこを区別するために、常に一個以上データのない場所を空けておく方法があります。この場合、24個のデータに対して、大きさが25の配列が必要になります。 皆無と満杯を区別するもう一つの方法はフラグを設けることです。No.3の回答とあなたのコードを比べてみれば、QueueStateフラグの使い方に違いがあることがわかるでしょう。 しかし、私ならQueueStateフラグを使いません。代りに、格納されているデータの個数をカウントする変数を設けます。その変数は、初期値が0(=キューが空)、enqueue()で+1し、dequeue()で-1する、こうすればCountQueue()はその値をreturnするだけです。 しかもこの変数を見れば、キューが空であることも満杯であることも、すぐ判定できます。これで処理全体がすっきりするはずです。 その他、気づいた点。dequeue()はBYTE型の値を返す関数なのに、異常時に単にreturn;してます。こういう事をしてると後で痛い目に遭います(笑)。 このようなケースでは次のどちらかの方法をとります。 (a)正常時には決して返すことの無い値(0とか-1など)をエラーとして返す。 しかし表現可能なすべての値がデータとしてありうる場合はこの方法をとれないので、 (b)関数が返す値を成功か失敗か(或いはエラーの種類)だけにする。 データを返すためのポインタを引数として渡してもらい、例えば int dequeue(BYTE *x) として、*x = UartSendBuf[Head]; でデータを返します。

techhouse
質問者

お礼

回答頂きありがとうございました。返事が遅くなりましてすみません。 実は、回答頂いた内容で > (b)関数が返す値を成功か失敗か(或いはエラーの種類) > だけにする。 > データを返すためのポインタを引数として渡してもらい、例えば > int dequeue(BYTE *x) > として、*x = UartSendBuf[Head]; でデータを返します。 この内容に関する意味を理解することができずにいまして、 特にポインタを使っていることで、自分のポインタの知識が乏しいことに気づき”http://dell-support.okwave.jp/qa5507699.html”でこの部分に関して別個に質問させて頂いておりました。 そこで、ポインタについてググッていたら http://www9.plala.or.jp/sgwr-t/c/sec10.html というページを見つけ、やっと理解することができるようになった気がします。 引数には、”*x = UartSendBuf[Head];”このプログラムで取得したデータを代入したい引数のアドレスを設定していたんですね!! それで、dequeue関数の戻り値は、このキューのステート状態を取得できるし、また同時にデータも取得するためにポインタを使っているんですね。これは便利ですね。 もう少し、ポインタに関する知識をつけたいと思います。

その他の回答 (3)

回答No.3

No.2 です。 一部訂正。 enqueue の、キュー・フルの判断が間違っていました。 やっぱり、横着は良くないですね。 キュー・フルの判断は、「次に書こうとするところが、 次に読むはずのところ」なので、 void enqueue(BYTE x) { if (QueueState == QSTATE_FULL) return; UartSendBuf[Tail]=x; Tail=(Tail+1)%MAX_BUF_NUM; /*添え字の循環*/ if(Tail == Head ) QueueState = QSTATE_FULL; else QueueState = QSTATE_VALID; //かってにつけました。 } です(たぶん) ただ、これは、「書いてしまってから、これで一杯」 と通知するパターンです。 書こうとしたら一杯で書けませんでした。 というパターンではありません。

回答No.2

キューを構成する場合、head や tail が、 どこをさしているのか、 ・今読む(書く)ところ、 ・次に読む(書く)ところ、 を明確にするのが大切です、 さて、投稿されたソースでは、おそらく、 head = tail = 0 として初期化されているのでは ないかと想像しますが、その場合、UartSendBuf[0] が(初回のみ)デッドスペースになっています。 ・最初にデータを押し込む  tail を +1 して、そこに押し込む  (つまり、UartSendBuf[1] に押し込む) ・最初に引き出すデータ head を +1 したところのデータ  (つまり、UartSendBuf[1] を送信) しかも、キュー・フルの条件判断が、 tail を + 1 した結果が、head と一致 なので、 UartSendBuf[1] ~ UartSendBuf[23] まで データが埋まった時点(ここまで、23個) で、tail + 1 が、(巡回させているので) 0 になり、この時点の head と一致します。 これで、UartSendBuf[0] はまだがら空きな のに、キュー・フルになってしまいます。 (24個目のデータは捨てられます) こういう事ですね。 キュー・フルの判断は、 「データを書こうとしているところに既に データがある」ですから、 head, tail が、「これからデータを書こ うと/読もうと」するところであれば、 void enqueue(BYTE x) { if(Tail==Head){ QueueState = QSTATE_FULL; return; } UartSendBuf[Tail]=x; Tail=(Tail+1)%MAX_BUF_NUM; /*添え字の循環*/ return; } BYTE dequeue(void) { BYTE x; if(Head==Tail){ QueueState = QSTATE_EMPTY; return; } x=UartSendBuf[Head]; Head=(Head+1)%MAX_BUF_NUM; return x; } です。 (それぞれ、+1 している位置に注意) あと、 UartSendBuf[Head]=0x00; //出力し終わったら0x00でクリアしておく。 は、不要です。(そのデータは次に上書きされるまで使われることはないから) さらに、キュー・フルを、ヘッダの位置関係と、 キューの数の両方で判断していますが、これは、 無駄です。 というか、設計的にうまくいかなくて、キューの数 でも判断しようかという、小手先の判断ではないか という気がします。 本来、どちらか一方だけで判断できるはずなので、 それができないと言うことは、「どこか間違っている」 のです。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

要は「キューが空の状態」と「キューがいっぱいの状態」を区別できればいいだけなので, キューに「データがあるかないか」という情報を付加する手もあります. この辺は「プログラムの見やすさ」とか「実際の動作速度」とかの兼ね合いもある.

関連するQ&A

  • ポインタ変数が引数に宣言されている関数の使い方。

    以前、キュープログラムで”BYTE dequeue(BYTE *x)”というのを作成した際に、この作り方で質問させていただきい、この関数だと、 BYTE dequeue(BYTE *x) { if(Head==Tail){ if(QueueState != QSTATE_FULL){ return QSTATE_EMPTY; } } *x=QBuf[Head]; Head=(Head+1)%MAX_BUF_NUM; return QSTATE_VALID; } この関数だと、QueueState = dequeue(&UartSendBuf[i]);というように使用すれば、配列変数のUartSendBuf[i]でiの数値で指定した配列番号のメモリ番地にdequeueしたい値を入れることができ、なおかつ、キュープログラムがちゃんと動作したかのステータス値を戻り値として出力できるということを教わり、戻り値が複数出力できるような関数が作れるので大変便利だなぁと思ったのですが、 ちょっと疑問があるのですが、 QueueState = dequeue(UartSendBuf[i]); というように&を付けずにこのプログラムを動かした場合、どのような動きをプログラムはしてしまうのでしょうか?

  • dequeue(BYTE *x)関数の戻り値について

    以前、キューに関する質問をさせていただいた時に、回答者の方から、 BYTE dequeue(void) { BYTE x; if(Head==Tail){ QueueState = QSTATE_EMPTY; return; } Head=(Head+1)%MAX_BUF_NUM; x=UartSendBuf[Head]; UartSendBuf[Head]=0x00; //出力し終わったら0x00でクリアしておく。 return x; } 私のdequeue関数でエラーが発生している場合に単なる”return”で戻しているということでご指摘頂いたのですが、下記する文章で回答者の方が教えて頂いた内容で理解できない部分があり、困っております。 ----(回答者さんの文章)------------------------------------- その他、気づいた点。dequeue()はBYTE型の値を返す関数なのに、異常時に単にreturn;してます。こういう事をしてると後で痛い目に遭います(笑)。 このようなケースでは次のどちらかの方法をとります。 (a)正常時には決して返すことの無い値(0とか-1など)をエラーとして返す。 しかし表現可能なすべての値がデータとしてありうる場合はこの方法をとれないので、 (b)関数が返す値を成功か失敗か(或いはエラーの種類)だけにする。 データを返すためのポインタを引数として渡してもらい、例えば int dequeue(BYTE *x) として、*x = UartSendBuf[Head]; でデータを返します。 -------------------------------------------------------------- 確かにコンパイラはreturnだけで返すのはまずいとワーニングを出しているので、作り変えてみたのですが、 BYTE dequeue(BYTE *x) { if(Head==Tail){ QueueState = QSTATE_EMPTY; return 0xFF; } Head=(Head+1)%MAX_BUF_NUM; *x=UartSendBuf[Head]; return *x; } このポインタ変数が入ってくるのがよくわからず困っています。*xがreturnとなっていますが、どのような戻り値が返ってくるのでしょうか? あと、この関数を使う場合、 dequeue(Head); このような引数を入れてあげればよいのでしょうか? また、キューからデータを取り出したい場合は、 testBuf[i]=UartSendBuf[Head]; test = dequeue(Head); このようにdequeue関数の前でデータを取り出してから、dequeue関数の戻り値で判定するという処理をするようにしなければいけないでしょうか?

  • キューのプログラムのif文でわからないところが

    いま、キューのプログラムを見ていてわからないところがあるのですが、 void IncBuf() { if(++Front >= MAX_BUF_NUM){Front = 0;} } このif文の場合、どのような動きをするのかが正直わかりません。 MAX_BUF_NUMは24なのですが、 この場合、if文に入るとFrontをインクリメントして、そのあとにMAX_BUF_NUMの大小を判定するという感じでしょうか? ・私がやりたいのは、if文に入ったら、Frontをまずインクリメントして、その値がMAX_BUF_NUMと同じになったら、Frontに0を入れたい という感じでつくってみたのですが、これであっているのでしょうか?

  • Cのソースコードについて

    以下のソースコードをかきました。 #include<stdio.h> #include<string.h> #define MAX 100005 typedef struct PP { int t; char name[100]; }P; P Q[MAX]; int head, tail, n; void enqueue(P u) { Q[tail] = u; tail = (tail + 1) % MAX; } P dequeue() { P x = Q[head]; head = (head + 1) % MAX; return x; } int min(int a, int b) { return a > b ? a : b; } int main() { int q, sum = 0 , w; scanf("%d %d", &n,&q); for (int i = 1; i <= n; i++){ scanf("%s", Q[i].name); scanf("%d", &Q[i].t); } head = 1; tail = n + 1; P u; while (head != tail) { u = dequeue(); w = min(q, u.t); sum += w; u.t -= w; if (u.t > 0)enqueue(u); else { printf("%s %d", u.name, sum); } } return 0; } これでVisual C++ でコンパイルしたところ特にエラーも起きず問題なく動作しました しかしAOJに提出してみたところコンパイルエラーになってしまい詰んでしまいました どこかダメそうなところがあれば教えてください

  • C言語での構造体

    C言語で、 キュー構造を作りたいのですが、うまくできません。 途中まで作ったのですが、うまく動きませんでした。 EnqueueやDequeueでデータの出し入れをするのですが、そのままではデータを取り出したときにデータが先頭に来ないので、refreshで先頭に持ってくるようにプログラムを組みました。 ----------------------------------------------------- #include<stdio.h> #define MAXQUEUE 10 typedef struct queue{ int head, tail; char entry[MAXQUEUE]; } Queue; //キュー構造にデータを入れる。 void Enqueue(char item,Queue *q){ q->entry[q->tail]=item; q->tail++; } //キュー構造からデータを取り出す。 void Dequeue(char *item,Queue *q){ *item=q->entry[q->head]; q->head++; } //キュー構造内のデータを先頭にずらす。 void refresh(Queue *q){ while(q->head==0){ q->entry[q->head-1]=q->entry[q->head]; q->head--; q->tail--; } } void main(){ Queue qu; Enqueue('w1',&qu); Enqueue('w2',&qu); Enqueue('w3',&qu); Enqueue('w4',&qu); Dequeue(&qu,&qu); Dequeue(&qu,&qu); refresh(&qu); } ---------------------------------------------------------------- mainからEnqueueやDequeueを呼び出すときに、引数として何を渡せばいいのでしょうか?構造体をそのまま渡してみたのですが、「error C2664: 'Dequeue' : 1 番目の引数を 'Queue *' から 'char *' に変換できません。(新しい機能 ; ヘルプを参照) 指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。」というエラーを吐いてしまいます。 分かりづらい説明で申し訳御座いませんが、ご回答宜しくお願いいたします。

  • 双方向リストのプログラミングのチェック

    指定したファイルを読み込み、一行ずつ構造体に保存し、表示させる。その後、キーボードから指定した行を削除し、処理後の結果を表示させる・・・といったプログラミングを作成しています。 通常の表示の部分はできましたが、後は指定した行の削除の部分ができません。 あらかじめHPで調べてあるので、削除するときは、その削除したい行をまたいで、前後のリストを繋げるといった概念的なのは大体理解できました。 自分なりに考えたり調べて、下のように打ちました。 ですが、キーボードから入力した値をどう関数に対応させていくのか、 delete_head,delete_tailの関数の部分に出る、head,tailは使えない的なエラーの対処の仕方がわからなく、詰まってます。 アドバイス、他にも直す場所等ありましたらお願いします。 #include<stdio.h> #include<stdlib.h> #define LINE 1000 typedef struct num{ char line[LINE]; struct num *next; struct num *prev; }Num; void normal(Num **,Num **,char *); void reverse(Num *,Num *,char *); void delete_head(void); void delete_tail(void); void delete(Num *target); int main(int argc,char *argv[]) { FILE *fp; char line[LINE]; int i=0,j; Num *head,*tail,*p; head=NULL; tail=NULL; fp=fopen("test.txt","r"); if(fp==NULL){ fprintf(stdout,"File not found.\n"); exit(1); } while(fgets(line,LINE,fp)!=NULL) { normal(&head,&tail,line); } p=head; while(p!=tail){ printf("%s\n",p->line); p=p->next; } printf("%s\n",p->line); p=tail; while(p!=head){ printf("%s\n",p->line); p=p->prev; } printf("%s\n",p->line); printf("input number\n"); scanf("%d",&j); p=head; for(i=0;i<=j;i++);{ p=p->next; } if(p==head){ delete_head(void); } else if(p==tail){ delete_tail(void); } else{ delete(p->prev); } p=head; while(p!=tail){ printf("%s\n",p->line); p=p->next; } printf("%s\n",p->line); fclose(fp); return 0; } void normal(Num **s,Num **e,char *g){ Num *tmp; int i; if(*s==NULL){ *s=(Num *)malloc(sizeof(Num)*1); (*e)=(*s); for(i=0;((g[i]!='\0')&&(g[i]!='\n'));i++){ (*s)->line[i]=g[i];} (*s)->next=*e; (*s)->prev=*s; } else{ tmp=(Num *)malloc(sizeof(Num)*1); for(i=0;((g[i]!='\0')&&(g[i]!='\n'));i++){ tmp->line[i]=g[i];} tmp->prev=(*e); (*e)->next=tmp; tmp->next=(*e); (*e)=tmp; } return ; } void reverse(Num *kan1,Num *kan2,char *g){ Num *tmp; int i; tmp=(Num *)malloc(sizeof(Num)*1); for(i=0;((g[i]!='\0')&&(g[i]!='\n'));i++){ tmp->line[i]=g[i];} tmp->prev=kan1; kan1->next=tmp; tmp->next=kan2; kan2->prev=tmp; return ; } void delete_head(void){ Num *second=head->next; if(secound==NULL){ free(head); head=NULL; tail=NULL; } else{ second->prev=NULL; free(head); head=second; } } void delete_tail(void){ Num *second_last=head->prev; if(second_last==NULL){ free(tail); head=NULL; tail=NULL; } else{ second_last->next=NULL; free(tail); tail=second_last; } } void delete(Num *target){ Num *after_target; Num *before_target; after_target=target->next; before_target=target->prev; after_target->prev=target->prev; before_target->next=target->next; free(target); }

  • C++のポインタの動作を教えてください

    ちょっと長いですが、ある部分の動作がわかりません そこを詳しく教えてください。 void queue::store(int i)の//リスト末尾に置くっていうところからで なぜ、if(tail)tail->next=item; tail=item; こう書くのかわからないです。 tail->next=itemの次にtail=itemを実行すると、tail->nextの内容も変わるのでは?と思っているのですがどうなっているのかわからないのでお願いします。 説明が下手ですがすみませんが教えてください。 //仮想関数の実例 #include <iostream> #include <cstdlib> #include <cctype> using namespace std; class list{ public: list *head; //リスト先頭へのポインタ list *tail; //リスと末尾へのポインタ list *next; //次項目へのポインタ int num; //格納される値 list(){ head=tail=next=NULL; } virtual void store(int i)=0; virtual int retrieve()=0; }; //キュー型リストの作成 class queue:public list{ public: void store(int i); int retrieve(); }; void queue::store(int i) { list *item; item=new queue; if(!item){ cout << "メモリ割り当てエラー" << endl; exit(1); } item->num=i; //リスト末尾に置く if(tail)tail->next=item; tail=item; item->next=NULL; if(!head)head=tail; } int queue::retrieve() { int i; list *p; if(!head){ cout << "リストは空です" << endl; return 0; } //リスト先頭から取り除く i=head->num; p=head; head=head->next; delete p; return i; } //スタック型リストの作成 class stack:public list{ public: void store(int i); int retrieve(); }; void stack::store(int i) { list *item; item=new stack; if(!item){ cout << "メモリ割り当てエラー" << endl; exit(1); } item->num=i; //スタックのような操作になるよう、リスト最前部におく if(head)item->next=head; head=item; if(!tail)tail=head; } int stack::retrieve() { int i; list *p; if(!head){ cout << "リストは空です" << endl; return 0; } //リスト先頭から取り除く i=head->num; p=head; head=head->next; delete p; return i; } class sorted:public list{ public: void store(int i); int retrieve(); }; void sorted::store(int i) { list *item; list *p,*p2; item=new sorted; if(!item){ cout << "メモリ割り当てエラー" << endl; exit(1); } item->num=i; //次項目のおき場所を見つける p=head; p2=NULL; while(p){ //中へ if(p->num>i){ item->next=p; if(p2)p2->next=item; //先頭要素ではない if(p==head)head=item; //新しい先頭要素 break; } p2=p; p=p->next; } if(!p){ //終わりへ if(tail)tail->next=item; tail=item; item->next=NULL; } if(!head)//先頭要素 head=item; } int sorted::retrieve() { int i; list *p; if(!head){ cout << "リストは空です" << endl; return 0; } //リスト先頭から取り除く i=head->num; p=head; head=head->next; delete p; return i; } int main() { list *p; //キューのデモ queue q_ob; p=&q_ob; //キューをさす p->store(1); p->store(2); p->store(3); cout << "キュー:"; cout << p->retrieve(); cout << p->retrieve(); cout << p->retrieve(); cout << endl; //スタックのデモ stack s_ob; p=&s_ob; //スタックをさす p->store(1); p->store(2); p->store(3); cout << "スタック:"; cout << p->retrieve(); cout << p->retrieve(); cout << p->retrieve(); cout << endl; //ソート済みリストのデモ sorted sorted_ob; p=&sorted_ob; p->store(4); p->store(1); p->store(3); p->store(9); p->store(5); cout << "ソ\ート:"; cout << p->retrieve(); cout << p->retrieve(); cout << p->retrieve(); cout << p->retrieve(); cout << p->retrieve(); cout << endl; return 0; }

  • 「シンボルを見つけれません」というエラーへの対処法

    下記のコードの If(type.compareTo("名詞") == 0); で「シンボルを見つけれません」「シンボル:メソッドif(boolean)」というエラーが出てしまいます。 なぜ、他のif文ではエラーが出ないのにこの行ではエラーが出るのか分かりません。 対処法を教えていただけないでしょうか? public void set_dictionary(String dict_filename) { } public int run(byte[] text, int start, int length, byte[] terms) { String type; //品詞の比較用 byte[] buf1 = new byte[1000]; //textの内容を一時的に格納しておくための配列1 byte[] buf2 = new byte[1000]; //textの内容を一時的に格納しておくための配列2 int i, j, k, count1, count2, FLG; count1 = 0; count2 = 0; k = 0; FLG = 0; for(i = start; i < length; i++) { if(text[i] == '\t') { FLG = 1; count2 = 0; } else if(text[i] == ' ') { FLG = 0; count1 = 0; } else { if(FLG == 1) { buf2[count2++] = text[i]; } else if(FLG == 0) { buf1[count1++] = text[i]; } } if((count2 == 5)&&(FLG == 1)) { type = new String(buf2, 0, 4); If(type.compareTo("名詞") == 0); { for(j = 0; j < count1; j++) { terms[k++] = buf1[j]; } terms[k++] = '/'; } count2 = 0; FLG = 2; } } return k; //term中のデータの長さを返す }

  • エラーのチェックについて

    1~10の数字以外が出たら、再入力を促すようにしたいのですが、上手くいかないので教えてください。 int main(void) { char buf[MAX], moji[MAX]; int n, num; printf("1から10の番号を入力してください:"); while (1) { fgets(buf, MAX, stdin); // 文字列の読み込み n = sscanf(buf, "%d%s", &num, moji); // 文字列から変換 if (num >= 1 && num <= 10 && n == 1) { // 範囲外のエラーチェック break; // 正なら終了 } else { // 間違っていたら入力し直し printf("1から10の番号を入力してください:"); } } 以上のようにして、aや11, ctrlキー+zを入力してEnterを押したときは良いのですが、aの後に続けてctrlキー+zを入力してEnterを押したらelseのprintfがずっと出力され続けてしまいます。それ以外に自分で試した入力の中にはエラーは出ませんでした。ctrlキー+zのことを良く理解していないということもあるのかもしれません。

  • 整数値を任意のオブジェクトを格納できるスタッククラスに格納する方法

    class ObjStack { private Object[] mBuf; private int mSP; public ObjStack(int size) { mSP = 0; mBuf = new Object[size]; } public int getSize() { return mBuf.length; } public int getNum() { return mSP; } public boolean isFull() { return mSP == mBuf.length; } public boolean isEmpty() { return mSP == 0; } public void push(Object x) { if (!isFull()) { mBuf[mSP++] = x; } } public Object top() { Object obj = null; if (!isEmpty()) { obj = mBuf[mSP-1]; } return obj; } public Object pop() { Object obj = null; if (!isEmpty()) { obj = mBuf[--mSP]; } return obj; } } 上のプログラムは任意のオブジェクトを格納できるスタッククラスです。 int型の値はオブジェクトではないので、このObjyStackクラスに格納することができない。 整数値をこのObjStackクラスに格納する方法を教えて下さい。

    • ベストアンサー
    • Java

専門家に質問してみよう