我有一些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 %sn",
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 %in", (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 %in", (int) line, (int) tok);
行总是打印相同的两个值,为什么这些指针地址永远不会改变?我已经确认parseInstruction每次都返回一个唯一的指针值,但每条指令的instr槽中都有相同的指针。
为了清楚起见,指令定义如下:
typedef struct Instruction {
char *instr;
char *arg1;
char *arg2;
}说明书;
我做错了什么?
strtok
就是这样工作的:它实际上修改了正在操作的字符串,用' '
替换分隔符,并返回指向该字符串的指针。(请参阅strtok(3)
手册页中的"BUGS"部分,尽管这并不是一个真正的错误,只是人们通常不会期望的行为。)因此,您的初始tok
将始终指向line
的第一个字符。
顺便说一下,这个:
char *tok = (char*) malloc(sizeof(tok));
tok = strtok(line, " ");
首先将tok
设置为指向malloc
的返回值,然后将其重新分配给指向strtok
的返回值的点,从而完全丢弃malloc
的返回值。就像写这个一样:
int i = some_function();
i = some_other_function();
完全丢弃CCD_ 11的返回值;除了更糟之外,因为丢弃malloc
的返回值会导致内存泄漏。
代码中有一些问题。最令人震惊的是parseInstruction
:
char *tok = (char*) malloc(sizeof(tok));
tok = strtok(line, " ");
在这里,您为tok
分配内存,但随后将tok
设置为等于strtok
的结果,这实际上会使您分配的内存无法访问(它从未使用过)。这不仅是内存泄漏,而且意味着tok == line
将始终为true,因为strtok
返回一个指向第一个令牌的指针(当使用第一个参数!=NULL
调用时,它通常是字符串的开头)。
这就解释了你的第一个问题(价值观是相同的)。关于你的值在重复迭代中总是相同的问题,Edmund的回答很好地总结了这一点:完全是偶然的,你在释放和破坏同一块内存。
您应该去掉char *tok = malloc(...)
行,只需让strtok
按预期对字符串进行操作即可。这将修复你的内存泄漏。不过,您在打印输出中仍然会看到相同的值,因为strtok
就是这样工作的。
不过,使用此代码最终会遇到的一个严重问题是,您正在使用指向line
内存空间的指针将分配给指令结构。这在循环的每次迭代中都很好,但当你在每次迭代结束时释放时,内存就会消失,只会被其他人回收。现在,它可能会意外地工作,但最终你会浑身湿透。如果你想让每个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)
有相同的效果),但这应该会让你走上正轨。
它必须是sizeof(char)
而不是sizeof(tok)
纠正
char *tok = (char*) malloc(sizeof(tok));
to
char *tok = (char*) malloc(sizeof(char));
您为一行分配内存,处理它,然后释放它。(您也为指令分配其他内存,但不释放它,但这是单独的。)
实际情况是,当你从行中释放内存时,会有一个200个字符大小的洞(加上一些记账空间)。您使用的malloc
会找到这个洞,并在循环中再次使用它。
这些都是单独的分配,但恰好每个分配都重用了内存中的相同空间。当然,这应该被视为一场意外:你所做的其他分配的改变可能会改变它