编辑:很抱歉没有提供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;
}