【问题标题】:Undefined computer behavior after running a C program运行 C 程序后未定义的计算机行为
【发布时间】:2016-06-16 00:01:53
【问题描述】:

我说得对,因为我无法解释我将要描述的情况。我需要你的关注!

昨天我用C写了一个程序。该程序将一个字符串作为输入,如果该字符串是这种形式的“PKPKKKPPPKKKP”,即由仅包含'P'和'K'字符,它会打印你是或否。 YES 如果单个“P”字符与“K”字符匹配。就像我们对括号字符问题'(', ')'所做的那样,只是我不得不使用'P'而不是'(',而不是')','K'。

在此处的一些帮助下,我设法完成了该程序并且它运行正常。我认为复制代码不会帮助任何人,但我会解释它是如何工作的。

节目说明: 程序接受一个字符串(字符串最长可达 500)作为输入,如果字符串只包含 'P' 和 'K' 字符,它会打印 YES 或 NO,如上所述,否则它拒绝它。然后它逐个字符地读取输入,当它找到“P”时,它被压入堆栈,否则弹出。 (我用链表实现了堆栈,因此用户将能够给出他想要的尽可能大的字符串(或者我是这么认为的......))。 在用户键入字符串之前,符号“A”在堆栈中。因此,输入由程序逐个字符地分析,如果找到一个“P”,则将其推入堆栈,否则弹出堆栈。如果最后的栈顶是 'A' 字符,则程序打印 YES,否则打印 NO。

问题描述: 所以我今天和我的一个朋友一起表演这个节目。一切都很好。直到我将一个非常大的字符串作为输入传递,例如 300'P's 和 300'K's(记住我的字符串是一个 char 字符串 [500])。它打印的是。然后我输入了一个像 800'P's+800'K's 这样的字符串。它没有正确运行。 这就是问题所在,在那次事件之后,无论我输入一个字符串,一个普通的“PKPKPK”,它都会打印出数百万个奇怪的符号( x └ X ╨ x └ X ╨ x )。我发誓我没碰过密码!我再次编译,再次运行,还是一样!我的电脑(Windows 10)好像出了点问题。 问题继续到另一个程序......我试图制作一个简单的程序,只是为了运行一个用链表实现的堆栈。我按下字符“a”并打印它。它打印'á'。我按了“b”。它打印'h'。我按了“d”。它打印“L”。

显然我不应该输入这么大的字符串,因为它的长度限制是 500。但问题仍然存在!我不能再用链表编写程序了。生无可恋!

代码:

#include "stdio.h"
#define FIRST_SYMBOL_IN_STACK 'A'
#define TRUE 1
#define FALSE 0
#define STR_LENGTH 50
#define YES "YES"
#define NO "NO"
#define PLA 'P'
#define KAL 'K'

typedef struct node {
    char simvolo_eisodou;
    struct node *next;
} node; 

node *top = NULL; // the top of the stack

void push(char simvolo_eisodou); //stack function
int isStackEmpty(); //stack function
void pop(); //stack function
void printStack(); //print the current stack elements
int isInputValid(char *string); 
void printWelcome();

int main() {
    char input_string[STR_LENGTH], apantisi = 'G';  //O xristis mporei na dwsei input_string mikous ews 500 xaraktires
    push(FIRST_SYMBOL_IN_STACK);
    int i = 0;
    scanf("%s", input_string);

    if (isInputValid(input_string)) {
        while (input_string[i] != '\0') {
            if (input_string[i] == PLA) {
                push(PLA);
                printStack();
            } else {
                pop();
                printStack();;
            }
            i++;
        }
    } else {
        printf("Den anagnwristike to %s, input_string=(P|K)*\n");
        _exit(-1);
    }

    if (top->simvolo_eisodou == FIRST_SYMBOL_IN_STACK) {
        printf("%s\n", YES);
    } else {
        printf("%s\n", NO);
    }

    return 0;
}

void push(char simvolo_eisodou) {
    node *newNode = (node*)malloc(sizeof(node));
    newNode->simvolo_eisodou = simvolo_eisodou;
    newNode->next = top;
    top = newNode;
    free(newNode);
}

int isStackEmpty() { //Thewrw oti i stoiva einai adeia otan i korifi einai to arhiko simvolo
    if (top->simvolo_eisodou == FIRST_SYMBOL_IN_STACK) {
        return TRUE;
    }
    return FALSE;
}

void pop( ){
    if (isStackEmpty()) {
        printf("KENO\n");
        printf("%s\n", NO);
        _exit(-1);
    }
    node *temp = top;
    top = top->next;
    free(temp);
}

void printStack() {
    node *current = top;
    while (current != NULL) {
        printf("%c ", current->simvolo_eisodou);
        current = current->next;
    }
    free(current);
    printf("\n");
}

int isInputValid(char *string) {
    int i = 0;
    while (*(string + i) != '\0') {
        if (!(*(string + i) == 'P' || *(string + i) == 'K')) {
            return 0;
        }
        ++i;
    }
    return 1;
}

void printWelcome() {
    printf("\n====================================================================\n");
    printf("Welcome\n");
    printf("====================================================================\n");
    printf("\n\n\n Plz type input_string=(P|K)*\n");
}

【问题讨论】:

  • “我认为复制代码不会帮助任何人”。这是一个非常错误的想法。问题几乎可以肯定是代码中的错误导致未定义的行为。请在问题本身中发布代码(而不是作为外部链接)。你认为在这段代码运行后top 指向的内存会发生什么:top=newNode; free(newNode);?然后,当您尝试访问top 指向的数据时,您认为稍后会发生什么?
  • 嗯,我想知道如果您尝试在数组中放入比数组中的项目更多的项目会发生什么,嗯,我想知道为什么这会给您带来问题...
  • @kaylum top=newNode;意味着顶部指向 newNode,所以我不再需要 newNode,因此我释放了它!对吗?
  • 没有。你毁了房子。指针只是物理房子的地址。查找“未定义的行为”。这意味着您无法预测结果会是什么 - 它可能会立即崩溃,不会崩溃但会产生错误的结果,有时似乎可以工作等等。并且行为可能会在重新编译或同一程序的不同运行等时发生变化。所以完全不可预测。

标签: c linked-list stack stack-overflow buffer-overflow


【解决方案1】:

您的代码具有未定义行为 (UB)。使用 UB 运行代码的结果是不可预测的。它有时看起来有效,但不能保证每次都会出现相同的结果。

您的 UB 的至少一个来源是此代码:

top=newNode;
free(newNode);

一旦newNode 被释放,top 指针就会变得无效,并且对该指针的任何取消引用都将导致 UB。

【讨论】:

    【解决方案2】:

    您的代码开始于:

    #define STR_LENGTH 50
    
    // ...  in main
    char input_string[STR_LENGTH];
    scanf("%s", input_string);
    

    在您的问题中,您谈到了长度为500 的缓冲区。但是你的代码中没有这样的缓冲区,长度是50

    如果您输入 50 个或更多字符,则会导致 undefined behaviour。这意味着任何事情都可能发生。在这种情况下,您无法控制发生的事情,并且您不应该期待任何特定的行为。

    解决此问题的唯一方法是停止缓冲区溢出。您必须更改您的代码,以免溢出。

    您的程序描述是它应该支持最多 500 个字符的字符串。实现这一目标的一种方法是:

    char input_string[501];   // +1 for terminator
    scanf("%500s", input_string);   // IMPORTANT: 500 limiter
    

    如果你想在他们输入太多时报告错误,而不是仅仅忽略它,你可以这样写:

    if ( !isspace(getchar()) )    // requires #include <ctype.h>
    {
        fprintf(stderr, "Too many characters entered - aborting program");
        exit(EXIT_FAILURE);
    }
    

    例如。

    如果您真的想支持任意长度的输入,那么您需要切换到更复杂的内存策略(例如,链表,或随着输入的增长重新分配缓冲区)。


    关于您的代码的其他说明:

    • 你使用来自#include &lt;stdlib.h&gt;的函数,所以你必须有那一行
    • printf("Den anagnwristike to %s, input_string=(P|K)*\n") 有一个 %s 但没有对应的参数,这也会导致未定义的行为
    • 使用exit(EXIT_FAILURE) 而不是_exit(-1);

    注意。可能还有其他问题,我没有检查你的整个程序。

    【讨论】:

    • 如果 OP 的代码是整个代码,那么应该避免使用微软的编译器。
    • @M.M 感谢您的努力。我知道溢出缓冲区也是一个问题,但有时它会起作用。我的问题是我无法理解 free() 是如何工作的。我认为它释放了指针的内存空间,而不是指针指向的位置。不幸的是,我只能将一个问题标记为正确!
    • @Michi:因为 MSVC 无论如何都不符合标准,所以总的来说这是个好建议。
    • @Olaf Alles klar :))。我永远不会使用 Microsoft 产品,这意味着我永远不会了解它。
    猜你喜欢
    • 2011-05-05
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多