运行C程序后未定义的计算机行为

时间:2016-03-02 22:00:53

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

我正确到了这一点,因为我无法解释我要描述的情况。我需要你注意!

昨天我在C中编写了一个程序。该程序将一个字符串作为输入,如果该字符串采用“PKPKKKPPPKKP”形式,即包含仅“P”和“K”字符你是或否。如果单个'P'字符与'K'字符匹配,则为YES。正如我们对括号字符问题'(',')'一样,只代替'('我必须使用'P'而不是')','K'。

从这里得到一点帮助,我设法完成了程序,它运行正常。我不认为复制代码会对任何人有所帮助,但我会解释它是如何工作的。

计划说明:  程序采用字符串(字符串最长可达500长度)作为输入,如果字符串仅包含'P'和'K'字符,则打印YES或NO,如上所述,否则它拒绝它。然后它逐字符地读取输入,当它找到'P'时,它被推入堆栈,否则弹出。 (我使用链表实现了堆栈,因此用户可以提供他想要的大字符串(或者我认为......))。  在用户键入字符串之前,符号“A”在堆栈中。所以输入是由程序逐个字符地分析,如果它发现'P'它被推送到堆栈,否则弹出堆栈。如果最后的堆栈顶部是'A'字符,则程序打印为YES,否则为NO。

问题描述:  所以我今天和我的一个朋友一起执行该计划。一切都很好。直到我作为输入传递一个非常大的字符串,如300'P和300'K(并记住我的字符串是一个字符串[500])。它打印是的。然后我输入了一个像800'P + 800'K的字符串。它运行不正常。  这就是问题,在那次事件之后,无论我输入一个正常的字符串“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");
}

2 个答案:

答案 0 :(得分:4)

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

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

top=newNode;
free(newNode);

释放newNode后,top指针变为无效,任何解除引用该指针都将导致UB。

答案 1 :(得分: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 <stdlib.h>中的功能,因此您必须拥有该行
  • printf("Den anagnwristike to %s, input_string=(P|K)*\n")%s但没有相应的参数,这也会导致未定义的行为
  • 使用exit(EXIT_FAILURE)代替_exit(-1);

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