C++ 程序在多次打印 (cout) 后停止(冻结、无错误、无退出)

C++ program stop (freeze, no error, no exit) after a number of print (cout)

我有这个测试阿克曼函数的代码,我称之为程序P1。在 ackerman(int m, int n) 中。 我的目的是,如果 OUT 没有定义,程序将 运行 并在完成后打印结果。如果定义了 OUT,程序将每隔 "loop" 打印 "xxx",并等待来自标准输入的字符输入。我有一个外部进程来读取 P1 的输出,捕获 "xxx" 字符串(做某事)并将一个字符写回 P1,这样它才能继续。 如果我没有定义 OUT 和 运行 P1 独立编译,代码可以完全 运行,并打印出 ack(3,6) = SomeValue,但是如果我启用 OUT,并且 运行带P1的外部进程,P1一会能收到char,突然"pause",不死机,不报错,不退出。我用c变量统计了P1停止前的次数,这不是一个固定的数字(3813、3951、3804……)。外部进程是用C#写的,测试的很好。我在很多情况下都使用过它,但在这个 ackermann 函数中,它停止了。为什么它不会在第一个循环中停止,而是在相当多的循环中停止?打印和停止有关系吗?

P1:

int MAX = 99999;
class stack {
private:
    int num[99999];
    int size;
public:
    stack() {
        size = 0;
    }
    void push(long x) {
        if(size < MAX)
            num[++size] = x;
        else {
            cout<<"Full Stack"<<endl;
            return;
        }
    }
    int pop() {
        if(size==0) {
            cout<<"Out of stack memory"<<endl;
            return -1;
        } 
        return num[size--];
    }
    bool isEmpty() {
        if(size == 0)
            return true;
        else
            return false;
    }
};

int ackman(int m,int n)
{
    stack v;
    v.push(m);
    int c = 0;
    while(!v.isEmpty()) {
        m = v.pop();
        if(m==0 ||n==0)
            n+= m+1;
        else {
            v.push(--m);
            v.push(++m);
            n--;
        }
        c++;

#ifdef OUT
        printf("c is %d\n",c );
        cout << endl;
        //flush(stdin);
        //flush(stdout);
        cout << "xxx" << endl;
        getchar(); 
        //sleep(200);
#endif
    }
    return n;
}

void ack() 
{
    int m = 3, n = 6;
#ifdef DEMO
    m = 3;
    n = 4;
#endif
    try{
        int k = ackman(m,n);
        printf("Ack %d , %d = %d\n",m,n,k );
    }catch(...){
        cout << "Co loi khi ack";
    }
}

//just example
int main(){
 ack();
return 0;
}


外部进程

static List<BenchMemory> benchMemory(String exeName, String caseName)
            {
                int count = 0;
                Console.Title = "Bench memory: " + exeName + " case: "+caseName;
                Console.WriteLine("Exe: {0}, case: {1}", exeName, caseName);
                using (Process process = new Process())
                {
                    /* Create the start info object */
                    ProcessStartInfo startInfo = new ProcessStartInfo();
                    startInfo.FileName = exeName + ".exe";
                    startInfo.Arguments = caseName;
                    startInfo.UseShellExecute = false;
                    startInfo.RedirectStandardOutput = true;
                    startInfo.RedirectStandardInput = true;
                    List<BenchMemory> listMemResult = new List<BenchMemory>();
                    #region handler
                    DataReceivedEventHandler outDataHandler = (object sender, DataReceivedEventArgs e) =>
                    {
                        if (e.Data != null)
                        {
                            count++;
                            Console.WriteLine(e.Data);
                            if (!process.HasExited && e.Data != null && e.Data.ToLower().Equals("xxx"))
                            {
                                var bMem = getProcessMemory(process, exeName, caseName);
                                listMemResult.Add(bMem);
                                process.StandardInput.WriteLine("k");
                            }


                        }
                    };
                    #endregion
                    process.StartInfo = startInfo;
                    process.OutputDataReceived += outDataHandler;
                    process.Start();
                    process.BeginOutputReadLine();
                    process.WaitForExit();
                    var topPeak = listMemResult.Take(10).ToList();
                    process.Refresh();
                    return topPeak;
                }
            }

在您的 C++ 程序中添加 fflush(stdin); 应该可以解决问题(下面是一段修改后的代码):

#ifdef OUT
        printf("c is %d\n",c );
        cout << endl;
        cout << "xxx" << endl;
        fflush(stdin); // note
        getchar();
#endif

出于调试目的,我还稍微修改了您的 C# 代码,但没有什么值得看的:

DataReceivedEventHandler outDataHandler = (object sender, DataReceivedEventArgs e) =>
{
    if (e.Data != null)
    {
        var senderProcess = sender as Process;

        if(sender == null)
            Console.Write("Sender == NULL?");

        if(senderProcess.HasExited)
            Console.WriteLine("Exited");

        count++;
        Console.WriteLine(e.Data);

        // e.Data != null redundant check, already everything wrapped in if(e.Data != null)
        if (e.Data.Trim().ToLower().Equals("xxx"))
        {
            senderProcess.StandardInput.Write("k");
            Console.WriteLine("Pass");
        }
    }
};

在用 Write 替换 WriteLine 之后,我第一次得到的数字总是 2047(WriteLine 打印两个字符,注意 \n ),我得到了 4095。再次是 2 的幂(加 +1)?缓冲?使用调试器 StandardOutput 检查,您会注意到输出缓冲区中确实有 4096 个字符。此外,如果将断点设置为 Write,您会注意到在 4095 之后它会卡在那里(使用条件断点来缓解您的生活)并且永远不会到达 Console.WriteLine("Pass").

StandardOutput.Flush 并没有真正解决问题(在文档中也找不到原因)而且 getchar 如果我没记错的话也没有清除它。

fflush(stdin) 添加到 C++ 程序会清除它,使 space 让 C# 继续写入。