見出し画像

(答案作成中)C言語教室 第23回 - スタックとキュー(キュー編、その2)

昨晩、ChatGPT様のお告げに従ってdequeue()関数の仕様を変更し、取得した値を参照渡しで返そうとしたけれども、相変わらず課題の本来の趣旨とは異なるポインタの設定で手こずってうまくいかない。

翌朝、フレッシュな気分でもう一度書き換えたら、アッサリ動くようになった。同じことをしていたはずなのに、今となってはなんでうまくいかなかったのかよくわからない。単に寝ぼけていたのか?

やはり、どハマりした時は、一晩寝てやり直すに限る(経験的鉄則)

dequeue()関数の仕様を変更しようと思ったのは、当初は戻り値を配列の配列番号にすれば(マイナス値はありえないから)取得した値と混乱することはないと考えたけれども、戻り値に二つの意味、即ち
 ① queue[]から取得した値
 ② queue[]が空で、値を取得できなかった結果
を持たせるのはスッキリしないと感じたから。

このようにすれば、dequeue()したあとにqueue[]内の値をリセットしやすくなるし、enqueue()/dequeue()の結果の検証も楽になる。

dequeue()関数の仕様を変更したバージョン

/**********************************************************************
	lesson23 Configure a queue with an array
		by Akio van der Meer

[変更履歴]
 (無印)  記事初投稿時のコード
 r1.0	bool dequeue(int *queue, int *val)の変更
	取得したqueueの値は、参照渡し(*val)で返す
	dequeue()した配列要素は、0にリセットする
	戻り値は、実行結果の論理値(成功;true / 失敗;false)とする

**********************************************************************/

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

#define Queue_MAX 10
int queue_top = 0;
int queue_bottom = 0;
int read_point = 0;
bool is_reverse = false;

int * init_queue(int queue_size) {
  int *arry;
	arry = (int*)malloc(sizeof(int) * queue_size);
	if (arry == NULL) exit(0);

  for (int i = 0; i < queue_size; i++) {
    arry[i] = 0;
  }
  return arry;
}

void enqueue(int * queue, int val) {
  if (is_reverse == false) {
    if (queue_top < Queue_MAX) {
      queue[queue_top++] = val;
      printf("enqueued (%d)\n", val);
    } else {
      queue_top = 0;
      is_reverse = true;
    }
  }

  if (is_reverse == true) {
    if (queue_top < queue_bottom) {
      queue[queue_top++] = val;
      printf("enqueued (%d)\n", val);
    } else {
      printf("Queue is FULL! (%d has not been queued.)\n", val);
    }
  }
}

bool dequeue(int *queue, int *val) {
  *val = 0;

  if (queue_bottom >= Queue_MAX) {
    queue_bottom = 0;
    is_reverse = false;
  }

  if (is_reverse == false) {
    if (queue_bottom < queue_top) {
        *val = queue[queue_bottom];
        queue[queue_bottom++] = 0;
        return true;
    } else {
      printf("Queue is Empty!\n");
      return false;
    }
  } else  {
    *val = queue[queue_bottom];
    queue[queue_bottom++] = 0;
    return true;
  }
}

void print_queue(int * queue) {
  read_point = queue_bottom;

  printf("\nprint_queue: ");

  if (is_reverse == true) {
    while (read_point < Queue_MAX) {
      printf("%02d ",queue[read_point++]);
    }

    read_point = 0;
    while (read_point < queue_top) {
      printf("%02d ",queue[read_point++]);
    }
  }

  if (is_reverse == false) {
    while (read_point < queue_top) {
      printf("%02d ",queue[read_point++]);
    }
  }

  printf("\n");
}

void dump_queue(int * queue) {
  printf("queue dump: ");
  for (int i = 0; i < 10; i++) {
    printf("%02d ", queue[i]);
  }
  printf("\n\n");
}

int main() {
  int cnt = 0;
  int rst = 0;
  int sample = 1;
  int *queue;
  queue = init_queue(Queue_MAX);

  printf("初期状態--------------\n");
  print_queue(queue);
  dump_queue(queue);

  printf("3回 取り出し--------------\n");
  for (cnt = 0; cnt < 3; cnt++) {
    if (dequeue(queue, &rst) == true) printf("dequeued (%d)\n", rst);
  }
  print_queue(queue);
  dump_queue(queue);

  printf("4回追加--------------\n");
  for (cnt = 0; cnt < 4; cnt++) {
    enqueue(queue, sample++);  
  }
  print_queue(queue);
  dump_queue(queue);

  printf("4回追加--------------\n");
  for (cnt = 0; cnt < 4; cnt++) {
    enqueue(queue, sample++);  
  }
  print_queue(queue);
  dump_queue(queue);

  printf("4回追加--------------\n");
  for (cnt = 0; cnt < 4; cnt++) {
    enqueue(queue, sample++);  
  }
  print_queue(queue);
  dump_queue(queue);

  printf("3回 取り出し--------------\n");
  for (cnt = 0; cnt < 3; cnt++) {
    if (dequeue(queue, &rst) == true) printf("dequeued (%d)\n", rst);
  }
  print_queue(queue);
  dump_queue(queue);

  printf("5回追加--------------\n");
  for (cnt = 0; cnt < 5; cnt++) {
    enqueue(queue, sample++);  
  }
  print_queue(queue);
  dump_queue(queue);

  printf("6回取り出し--------------\n");
  for (cnt = 0; cnt < 6; cnt++) {
    if (dequeue(queue, &rst) == true) printf("dequeued (%d)\n", rst);
  }
  print_queue(queue);
  dump_queue(queue);

  printf("7回追加--------------\n");
  for (cnt = 0; cnt < 7; cnt++) {
    enqueue(queue, sample++);  
  }
  print_queue(queue);
  dump_queue(queue);

  printf("11回取り出し--------------\n");
  for (cnt = 0; cnt < 11; cnt++) {
    if (dequeue(queue, &rst) == true) printf("dequeued (%d)\n", rst);
  }
  print_queue(queue);
  dump_queue(queue);

  printf("3回追加--------------\n");
  for (cnt = 0; cnt < 3; cnt++) {
    enqueue(queue, sample++);  
  }
  print_queue(queue);
  dump_queue(queue);

  printf("4回取り出し--------------\n");
  for (cnt = 0; cnt < 4; cnt++) {
    if (dequeue(queue, &rst) == true) printf("dequeued (%d)\n", rst);
  }
  print_queue(queue);
  dump_queue(queue);


  //終了処理
  free(queue);
  return (0);
}


実行結果


jm3nrhMac-mini-:c akio$ gcc lesson23_r1.1.c
jm3nrhMac-mini-:c akio$ ./a.out
初期状態--------------

print_queue: 
queue dump: 00 00 00 00 00 00 00 00 00 00 

3回 取り出し--------------
Queue is Empty!
Queue is Empty!
Queue is Empty!

print_queue: 
queue dump: 00 00 00 00 00 00 00 00 00 00 

4回追加--------------
enqueued (1)
enqueued (2)
enqueued (3)
enqueued (4)

print_queue: 01 02 03 04 
queue dump: 01 02 03 04 00 00 00 00 00 00 

4回追加--------------
enqueued (5)
enqueued (6)
enqueued (7)
enqueued (8)

print_queue: 01 02 03 04 05 06 07 08 
queue dump: 01 02 03 04 05 06 07 08 00 00 

4回追加--------------
enqueued (9)
enqueued (10)
Queue is FULL! (11 has not been queued.)
Queue is FULL! (12 has not been queued.)

print_queue: 01 02 03 04 05 06 07 08 09 10 
queue dump: 01 02 03 04 05 06 07 08 09 10 

3回 取り出し--------------
dequeued (1)
dequeued (2)
dequeued (3)

print_queue: 04 05 06 07 08 09 10 
queue dump: 00 00 00 04 05 06 07 08 09 10 

5回追加--------------
enqueued (13)
enqueued (14)
enqueued (15)
Queue is FULL! (16 has not been queued.)
Queue is FULL! (17 has not been queued.)

print_queue: 04 05 06 07 08 09 10 13 14 15 
queue dump: 13 14 15 04 05 06 07 08 09 10 

6回取り出し--------------
dequeued (4)
dequeued (5)
dequeued (6)
dequeued (7)
dequeued (8)
dequeued (9)

print_queue: 10 13 14 15 
queue dump: 13 14 15 00 00 00 00 00 00 10 

7回追加--------------
enqueued (18)
enqueued (19)
enqueued (20)
enqueued (21)
enqueued (22)
enqueued (23)
Queue is FULL! (24 has not been queued.)

print_queue: 10 13 14 15 18 19 20 21 22 23 
queue dump: 13 14 15 18 19 20 21 22 23 101回取り出し--------------
dequeued (10)
dequeued (13)
dequeued (14)
dequeued (15)
dequeued (18)
dequeued (19)
dequeued (20)
dequeued (21)
dequeued (22)
dequeued (23)
Queue is Empty!

print_queue: 
queue dump: 00 00 00 00 00 00 00 00 00 00 

3回追加--------------
enqueued (25)
enqueued (26)
enqueued (27)

print_queue: 25 26 27 
queue dump: 26 27 00 00 00 00 00 00 00 25 

4回取り出し--------------
dequeued (25)
dequeued (26)
dequeued (27)
Queue is Empty!

print_queue: 
queue dump: 00 00 00 00 00 00 00 00 00 00 

jm3nrhMac-mini-:c akio$ 


ChatGPT様からのコメント

このコードの潜在的なバグは、以下の2点です。
1. queue_top が Queue_MAX 以上になる可能性がある
2. queue_bottom が Queue_MAX 以上になる可能性がある


queue_top と queue_bottom は、キューの先頭と末尾を表しています。キューは配列で表されているため、キューに要素を追加するたびに queue_top を増やし、要素を取り出すたびに queue_bottom を増やします。しかし、コードでは、queue_top が Queue_MAX に達した場合に、配列の先頭に戻って要素を追加するために、queue_top を 0 に戻しています。同様に、queue_bottom が Queue_MAX に達した場合に、配列の先頭に戻って要素を取り出すために、queue_bottom を 0 に戻しています。このとき、queue_top や queue_bottom が 0 に戻った後にもう一度増やされると、Queue_MAX を超えてしまい、配列の範囲外にアクセスしてしまいます。この場合、予期しない動作が発生する可能性があります。これを回避するためには、queue_top と queue_bottom を Queue_MAX - 1 まで増やすように修正する必要があります。また、コード内で配列の範囲外にアクセスしないように、enqueue() と dequeue() の実装を改善する必要があります。

随分と減りましたね。
うーん、この状況にはならないと思うんだけど、どうかな?

でも、送信した直後にコード解析した結果を表示してくれて、それも結構ツボを押さえた感じで、ChatGPTって凄いね。


ここまで読んでいただき、有り難うございました。

この記事が参加している募集

#AIとやってみた

27,969件

これまでの収益は全て、それを必要としておられる方々へ、支援機関を通して寄付させていただきました。この活動は今後も継続したいと思っています。引き続きよろしくお願いいたします。