getc(stdin)似乎在篡改堆

时间:2016-08-28 05:03:11

标签: c memory-management

编辑:很抱歉没有提供MCVE。我在这里创建了一个 - http://pastebin.com/qxzPkfBW。在问题的最后发表了完整的文章。

我正在编写一个布尔逻辑解析器/求值器来学习一些C.有一个词法分析器将输入解析为一个令牌结构流。该流是一个令牌**流(指向需要时在堆上创建的令牌结构的指针数组)。流中的最后一个令牌是一个特殊的End Of Stream令牌。

我的程序是segfaulting,因为EOS令牌被设置为空指针。调试后,我发现通过符号表并分配值的函数导致了问题。符号表是另一个标记**,它只包含指向扫描程序生成的流中变量的指针。

最初看起来像这样:

void assignVariables()
{
    for (int i = 0; i < symbolPointerIndex; i++)
    {
        printf("Is %s true or false?", symbols[i]->name);

        char answer = getc(stdin);
        int c;
        while ((c = getc(stdin)) != '\n' && c != EOF); //flush the rest of the input

        switch (answer)
        {
            case 't': case 'T': case '1':
                symbols[i]->value = 1;
                break;
            default:
                symbols[i]->value = 0;
        }
    }
}

我把它改成了这个,“解决了”问题:

void assignVariables()
{
    for (int i = 0; i < symbolPointerIndex; i++)
    {
        printf("Is %s true or false?\n", symbols[i]->name);
        symbols[i]->value = i % 2;
    }
}

出于好奇,我添加了getc(stdin),它再次使EOS令牌无效:

void assignVariables()
{
    for (int i = 0; i < symbolPointerIndex; i++)
    {
        printf("Is %s true or false?\n", symbols[i]->name);
        symbols[i]->value = i % 2;
        getc(stdin);
    }
}

我显然对手动内存管理很陌生,但我完全不知道getc(stdin)是如何使for循环甚至不触摸的指针无效。我已经检查过循环只运行变量标记,而没有运算符/ EOS。

提前感谢任何见解。

MCVE:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef enum {VAR, OP, EOS} tokenType;
static char* typeNames[] = {"VAR", "OP", "EOS"}; //debugging purposes

typedef struct 
{
    tokenType type;
    char* name;
    int value;
} token;

void printStream(token** stream)
{
    int i = 0;
    token* last;
    do
    {
        last = stream[i++];
        printf("Type: %s    Name: %s   Value: %d   Location: %p\n", 
            typeNames[last->type], last->type == VAR ? last->name : "Not a var", 
            last->value, last);
    } while (last->type != EOS);
}


#define SYMBOLSCHUNKSIZE 4
static int symbolsRemaining = 0;
static int symbolPointerChunks = 0;
static int symbolPointerIndex = 0;
static token** symbols = NULL;
token* createIdentifier(char name[])
{
    //check if it's already in the table
    int searchPointer;
    for (searchPointer = 0; searchPointer < symbolPointerIndex; searchPointer++)
        if (!strcmp(name, symbols[searchPointer]->name)) 
            return symbols[searchPointer];

    if (!symbolsRemaining)
    {
        if ((symbols = realloc(symbols, (++symbolPointerChunks * SYMBOLSCHUNKSIZE) * sizeof(token*))) == NULL)
        {
            fprintf(stderr, "Failed to extend allocated memory for the symbol pointer array!\n");
            free(symbols);
            exit(-1);
        }

        symbolsRemaining = SYMBOLSCHUNKSIZE - 1;
    }

    token* newID = malloc(sizeof(token));
    newID->type = VAR;
    newID->value = -1;
    newID->name = malloc(sizeof(char) * (strlen(name) + 1));

    strcpy(newID->name, name);
    newID->name[strlen(name)] = '\0';

    symbols[symbolPointerIndex++] = newID;

    return newID;
}

void assignVariables()
{
    for (int i = 0; i < symbolPointerIndex; i++)
    {
        printf("Is %s true or false?\n", symbols[i]->name);
        symbols[i]->value = i % 2;
        //getc(stdin); /*UNCOMMENTING THIS CAUSES ISSUES*/
    }
}

#define CHUNKLENGTH 8
token** lex()
{
    int chunks = 1;
    int tokensRemaining = CHUNKLENGTH;
    int streamPointer = 0;
    token** stream = malloc(sizeof(token*) * CHUNKLENGTH);

    char c;

    while ((c = getc(stdin)) != '\n')
    {
        if (isspace(c)) continue;

        if (isalpha(c))
        {
            char name[9];
            name[0] = c;

            int nPointer;
            for (nPointer = 1; isalpha(c = getc(stdin)) && nPointer < 8; nPointer++)
                name[nPointer] = c;
            name[nPointer] = '\0';

            //consume remaining characters if the identifier is too long
            while (isalpha(c)) c = getc(stdin);

            //ask the symbol table for a token for this identifier
            stream[streamPointer++] = createIdentifier(name);

            if (c == '\n') break;

            ungetc(c, stdin);
            continue;

        }

        //usually I'd check the different operators, trying to minimise this
        token* new = malloc(sizeof(token));
        new->type = OP;

        stream[streamPointer++] = new;

        if (!--tokensRemaining)
        {
            if ((stream = realloc(stream, (++chunks * CHUNKLENGTH) * sizeof(token*))) == NULL)
            {
                fprintf(stderr, "Failed to extend allocated memory for the token stream!\n");
                free(stream);
                exit(-1);
            }
            tokensRemaining = CHUNKLENGTH;
        }

    }

    token* eos = malloc(sizeof(token));
    eos->type = EOS;
    stream[streamPointer] = eos;

    return stream;
}

int main()
{
    token** stream = lex();

    printStream(stream); //fine here, even when done twice

    assignVariables();

    printStream(stream); //segfault due to missing EOS

    return 0;
}

0 个答案:

没有答案