0

我有一些 C 代码来解析文本文件,首先逐行解析,然后解析为令牌

这是逐行解析它的函数:

int parseFile(char *filename) {
//Open file
FILE *file = fopen(filename, "r");
//Line, max is 200 chars
int pos = 0;
while (!feof(file)) {
    char *line = (char*) malloc(200*sizeof(char));
    //Get line
    line = fgets(line, 200, file);
    line = removeNewLine(line);
    //Parse line into instruction
    Instruction *instr = malloc(sizeof(instr));
    instr = parseInstruction(line, instr);
    //Print for clarification
    printf("%i: Instr is %s arg1 is %s arg2 is %s\n", 
        pos,
        instr->instr,
        instr->arg1,
        instr->arg2);
    //Add to end of instruction list
    addInstruction(instr, pos);
    pos++;
    //Free line
    free(line);
}
return 0;

}

这是将每一行解析为一些标记并最终将其放入指令结构的函数:

Instruction *parseInstruction(char line[], Instruction *instr) {
//Parse instruction and 2 arguments
char *tok = (char*) malloc(sizeof(tok));
tok = strtok(line, " ");
printf("Line at %i tok at %i\n", (int) line, (int) tok);
instr->instr = tok;
tok = strtok(NULL, " ");
if (tok) {
    instr->arg1 = tok;
    tok = strtok(NULL, " ");
    if(tok) {
        instr->arg2 = tok;
    }
}
return instr;

}

ParseInstruction 中的行printf("Line at %i tok at %i\n", (int) line, (int) tok);总是打印相同的两个值,为什么这些指针地址永远不会改变?我已经确认 parseInstruction 每次都返回一个唯一的指针值,但是每条指令的 instr 槽中都有相同的指针。

为清楚起见,指令定义如下:

typedef struct Instruction {

char *instr;
char *arg1;
char *arg2;

} 操作说明;

我究竟做错了什么?

4

4 回答 4

4

这就是strtok工作原理:它实际上修改了它正在操作的字符串,用分隔符替换分隔符'\0'并将指针返回到该字符串。(请参阅手册页中的“BUGS”部分strtok(3),尽管它不是真正的错误,只是人们通常不期望的行为。)因此,您tok的首字母将始终指向line.

顺便说一句,这个:

char *tok = (char*) malloc(sizeof(tok));
tok = strtok(line, " ");

首先将其设置tok为指向 的返回值malloc,然后将其重新分配为指向 的返回值strtok,从而完全丢弃 的返回值malloc。就像这样写:

int i = some_function();
i = some_other_function();

some_function()完全丢弃;的返回值 除了更糟糕的是,因为丢弃的返回值会malloc导致内存泄漏

于 2012-12-03T03:00:37.157 回答
2

我可以通过代码看到一些问题。最令人震惊的是parseInstruction

char *tok = (char*) malloc(sizeof(tok));
tok = strtok(line, " ");

在这里,您为 分配内存tok,但随后设置tok等于 的结果strtok,这实际上使您分配的内存无法访问(它从未使用过)。这不仅是内存泄漏,而且意味着这tok == line将永远是正确的,因为strtok返回一个指向第一个标记的指针(当使用第一个参数 != 调用时,它NULL通常是字符串的开头)。

这解释了您的第一个问题(值相同)。至于您的值在重复迭代中始终相同的问题,Edmund 的回答很好地总结了它:完全是偶然的,您正在释放和分配同一块内存。

您应该摆脱这char *tok = malloc(...)条线,只需strtok按预期在适当的位置对字符串进行操作即可。这将解决您的内存泄漏问题。不过,您仍然会在打印输出中看到相同的值,因为这就是strtok工作原理。

但是,使用此代码最终会遇到的一个严重问题是,您使用指向. line这在循环的每次迭代期间都可以正常工作,但是当您在每次迭代结束时释放时,该内存就会消失,只会被其他人回收。它现在可能会偶然地起作用,但最终你会到处都是SEGFAULT。如果你希望每个struct Instruction人都有自己的私人记忆,你会想做更多这样的事情:

// You already do this part, but with a bug; use this code instead
Instruction *instr = (Instruction *)malloc(sizeof(Instruction));

// Then, inside parseInstruction, make these relevant changes
instr->instr = (char *)malloc(strlen(tok) + 1);
strcpy(instr->instr, tok);

instr->arg1 = (char *)malloc(strlen(tok) + 1);
strcpy(instr->arg1, tok);

instr->arg2 = (char *)malloc(strlen(tok) + 1);
strcpy(instr->arg2, tok);

这里的要点是,您希望为malloc结构中的每个字段创建一个新的内存块,大小strlen(...) + 1(“+1”用于 NUL 字节),然后将字符串复制到该新内存中。

当需要释放时,请确保释放所有内存:

// free each field
free(instr->instr);
free(instr->arg1);
free(instr->arg2);

// free the struct itself
free(instr);

可以做一些其他的事情来清理你的代码(例如,你的很多分配是不必要的:fgets(line, 200, file)与 具有相同的效果line = fgets(line, 200, file)),但这应该会让你走上正轨。

于 2012-12-03T03:24:25.847 回答
1

您正在为一行分配内存,处理它,然后释放它。(您还为指令分配其他内存并且不释放它,但这是分开的。)

发生的事情是,当您从行中释放内存时,会出现一个大小为 200 个字符的孔(加上一些簿记空间)。你正在使用的malloc找到这个洞并在下次循环中重用它。

这些都是单独的分配,但恰巧内存中的相同空间被每个分配重用。当然,这应该被视为意外:您所做的其他分配的更改可能会改变它。

于 2012-12-03T03:06:18.983 回答
1

一定sizeof(char)不是sizeof(tok)

纠正它

char *tok = (char*) malloc(sizeof(tok));
    to

char *tok = (char*) malloc(sizeof(char));
于 2012-12-03T02:59:29.617 回答