2012-12-03 104 views
0

我有一些C代码解析的文本文件,第一逐行然后进入令牌malloc的不断返回同一指针

这是由线解析它行中的功能:

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

那是怎样strtok作品:它实际上改变它的经营上,与'\0'更换分隔字符并返回指针到该字符串串。 (请参阅the "BUGS" section in the strtok(3) manual page,虽然它不是一个真正的bug,只是一种人们通常不期望的行为)。因此,您的初始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的返回值导致memory leak

+0

谢谢!我没有意识到strtok的行为,所以我必须在将它设置为我的指令的一个字段之前对新字符串进行操作,否则它们都指向同一个地方(由于我的mallocing和freeing)。我曾假设strtok会为tok创建一个新的char *,而不仅仅是指向一个位置。 –

1

它必须是sizeof(char)没有sizeof(tok)

正确它

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

char *tok = (char*) malloc(sizeof(char)); 
+0

没错,那更好,但不是问题。虽然谢谢! –

1

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

发生什么事是当你从行中释放内存,有一个大小为200个字符的洞(加上一些簿记空间) 。您正在使用的malloc找到了此漏洞,并在下一次循环中重新使用它。

这些都是单独的分配,但它只是发生在内存中相同的空间被重用每一个。当然,这应该视为一种意外事件:您对其他分配的更改可能会改变它。

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)具有相同的效果,但这应该会让您走上正确的轨道。

+0

谢谢你的建设性批评!我对C非常非常陌生(大部分都是用Java工作),所以我总是犯malloc错误。我改变了你说的话,并且会在我的代码库中寻找类似的错误。 –