【问题标题】:Can't find the solution for my guessing game找不到我的猜谜游戏的解决方案
【发布时间】:2020-08-12 21:31:36
【问题描述】:

我正在制作一个不同版本的猜谜游戏。这一次,子进程必须将它的猜测发送给父进程,然后父进程对其进行评估。我认为我做错的是我的孩子只跑了一次,但在找到正确的数字之前无法弄清楚如何猜测。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>

#define KEY 19950914
#define FLAG 0666

struct message {
    long mtype;
    int szam;
};

int main()
{
    int number, false=1, guess=0;
    int mqid;
    struct message buf;
    struct msqid_ds statbuff;

    mqid = msgget(KEY, FLAG | IPC_CREAT);

    if (mqid < 0)
            perror("msgget"), exit(EXIT_FAILURE);

    srand(time(NULL));
    number = rand() % 256;

    if (fork() == 0)
    {
            srand(time(NULL));
            buf.mtype = 2;
            buf.szam = rand() % 256;
            msgsnd(mqid, &buf, sizeof(struct message), 0);
            msgctl(mqid, IPC_STAT, &statbuff);

    exit(EXIT_SUCCESS);
    }

    while ( guess != number )
    {
            if (guess > number)
                    printf("Too high!\n");
            else if (guess < number)
                    printf("Too low!\n");

            guess = msgrcv(mqid, &buf, sizeof(struct message), 2, 0);
    }

    printf("Winner! Yes, the answer was %d \n",number);

    wait(NULL);

    exit(EXIT_SUCCESS);
}

【问题讨论】:

  • 我强烈建议不要使用名为 false 的变量。
  • 为什么要将此问题标记为bash?也许c 会更合适?
  • 感谢您的关注。那是因为我用bash写代码,但确实是C文件。
  • msgsnd(mqid, &amp;buf, sizeof(struct message)0); 您的代码无法编译,因为该行有错字。请发布您实际运行的代码。
  • 已更正,编辑时误删。

标签: c fork message-queue sysv-ipc msgrcv


【解决方案1】:

一种方法是让孩子进入一个循环,一旦你得到正确的答案就删除消息队列,这将使msgsnd失败,EIDRM退出循环:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>

#define FLAG 0666

struct message {
    long mtype;
    int szam;
};

int main()
{
    int number, false=1, guess;
    int mqid;
    struct message buf;

    mqid = msgget(IPC_PRIVATE, FLAG | IPC_CREAT);

    if (mqid < 0)
            perror("msgget"), exit(EXIT_FAILURE);

    srand(time(NULL));
    number = rand() % 256;

    if (fork() == 0)
    {
            buf.mtype = 2;
            int sndres;
            do {
                    buf.szam = rand() % 256;
                    sndres = msgsnd(mqid, &buf, sizeof(struct message), 0);
            } while(sndres == 0);

    exit(EXIT_SUCCESS);
    }

    do {
            msgrcv(mqid, &buf, sizeof(struct message), 2, 0);
            guess = buf.szam;
            if (guess > number)
                    printf("Too high!\n");
            else if (guess < number)
                    printf("Too low!\n");
    } while ( guess != number );

    printf("Winner! Yes, the answer was %d \n",number);

    msgctl(mqid, IPC_RMID, NULL);

    wait(NULL);

    exit(EXIT_SUCCESS);
}

我也在你的程序中修复了一些其他的东西:

  • 我没有使用固定的KEY,而是改为IPC_PRIVATE,这样就避免了key冲突的可能性。由于您没有尝试在其他地方打开相同的队列,因此没有理由使用固定的队列。
  • 我摆脱了statbuff 和你的IPC_STAT 电话。他们没有做任何有用的事情。
  • 我删除了您对srand 的第二次呼叫。通过将两个如此靠近,time(NULL) 两次都相同,因此您的子程序将具有相同的随机数状态,因此每次第一次尝试时都会猜对。
  • 成功的msgrcv 的返回值是消息的大小,它总是相同的(可能是16)。我在buf.szam 中更改了它以检查实际猜测。
  • 您对guess 的第一次检查是在您的第一次msgrcv 之前进行的,这导致了一个并非来自孩子的虚假猜测。我将您的 while 循环更改为 do-while 循环以避免这种情况。

这里还有一些需要修复的地方,但我留给读者作为练习:

  • 去掉所有你不实际使用的东西,比如false(顺便说一下,一个可怕的变量名称)
  • 不要像perror("msgget"), exit(EXIT_FAILURE);那样对逗号如此“聪明”。只需使用大括号和分号即可。
  • 你应该将fork()的结果保存到一个变量中,这样你就可以检查它是否为负数,这表明失败了。
  • 传递给msgsndmsgrcv 的大小应该是消息结构的第二个成员的大小(即,不包括mtype 或紧随其后的填充),而不是整个结构。
  • 您应该检查msgrcv 的返回以确保它不会失败。
  • 像我一样在恒定循环中运行子程序是最简单的方法,但不一定是最有效或最好的方法。考虑让父级向子级发送消息,以便它一次只发出一个猜测,而不是尽可能多地填充队列。 (即使您确实进行了此更改,您仍然应该让父级在最后删除消息队列,否则它不会消失,直到您重新启动或使用 ipcrm 手动清理它。)

【讨论】:

  • 感谢您对我做错的事情进行如此全面的审查。老实说,这个社区非常棒,我从这里得到的帮助对我们来说是一个巨大的帮助,因为我们不能上大学,也很难理解我们应该真正做什么。
猜你喜欢
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-01
  • 2015-04-15
  • 2020-11-22
  • 2014-05-12
相关资源
最近更新 更多