据我所知,很多人抱怨strcpy,但我没有找到任何使用搜索来解决我的问题。
首先,调用strcpy本身不会导致任何类型的崩溃/分段错误。其次,代码包含在一个函数中,第一次调用这个函数它完美地运行。它只是第二次崩溃。
我正在使用LPC1788微控制器进行编程;内存非常有限,所以我可以看到为什么像malloc这样的东西可能会失败,但不是免费的。
函数trimMessage()包含代码,函数的目的是删除大字符串数组的一部分,如果它变得太大。
void trimMessage()
{
int trimIndex;
// currMessage is a globally declared char array that has already been malloc'd
// and written to.
size_t msgSize = strlen(currMessage);
// Iterate through the array and find the first newline character. Everything
// from the start of the array to this character represents the oldest 'message'
// in the array, to be got rid of.
for(int i=0; i < msgSize; i++)
{
if(currMessage[i] == '\n')
{
trimIndex = i;
break;
}
}
// e.g.: "\fProgram started\r\nHow are you?\r".
char *trimMessage = (char*)malloc((msgSize - trimIndex - 1) * sizeof(char));
trimMessage[0] = '\f';
// trimTimes = the number of times this function has been called and fully executed.
// freeing memory just below is non-sensical, but it works without crashing.
//if(trimTimes == 1) { printf("This was called!\n"); free(trimMessage); }
strcpy(&trimMessage[1], &currMessage[trimIndex+1]);
// The following line will cause the program to crash.
if(trimTimes == 1) free(trimMessage);
printf("trimMessage: >%s<\n", trimMessage);
// Frees up the memory allocated to currMessage from last iteration
// before assigning new memory.
free(currMessage);
currMessage = malloc((msgSize - trimIndex + 1) * sizeof(char));
for(int i=0; i < msgSize - trimIndex; i++)
{
currMessage[i] = trimMessage[i];
}
currMessage[msgSize - trimIndex] = '\0';
free(trimMessage);
trimMessage = NULL;
messageCount--;
trimTimes++;
}
感谢所有帮助过的人。该功能现在正常工作。对于那些问我为什么要打印出一个我刚刚释放的数组的人来说,就是在那里显示问题发生在strcpy之后并排除了之后的任何其他代码。
最终的代码在这里,以防它对遇到类似问题的人有用:
void trimMessage()
{
int trimIndex;
size_t msgSize = strlen(currMessage);
char *newline = strchr(currMessage, '\n');
if (!newline) return;
trimIndex = newline - currMessage;
// e.g.: "\fProgram started\r\nHow are you?\r".
char *trimMessage = malloc(msgSize - trimIndex + 1);
trimMessage[0] = '\f';
strcpy(&trimMessage[1], &currMessage[trimIndex+1]);
trimMessage[msgSize - trimIndex] = '\0';
// Frees up the memory allocated to currMessage from last iteration
// before assigning new memory.
free(currMessage);
currMessage = malloc(msgSize - trimIndex + 1);
for(int i=0; i < msgSize - trimIndex; i++)
{
currMessage[i] = trimMessage[i];
}
currMessage[msgSize - trimIndex] = '\0';
free(trimMessage);
messageCount--;
}
答案 0 :(得分:10)
free can会崩溃。
考虑到这一点,我认为你的第一个malloc是几个字节短。您需要为空终止符保留一个字节,并且还要复制到偏移量1,因此您需要为此保留另一个字节。所以会发生的是你的副本将在下一个堆块的开头覆盖信息(通常用于下一个堆块的长度以及是否使用它的指示,但这取决于你的RTL )。
当你下一次免费时,它可能会尝试合并任何免费的块。不幸的是,你已经破坏了下一个块标题,此时它会有点疯狂。
答案 1 :(得分:2)
比较代码的这两行(我当然重新占用了第二行):
char *trimMessage = (char*)malloc((msgSize - trimIndex - 1) * sizeof(char));
currMessage = malloc((msgSize - trimIndex + 1) * sizeof(char));
除了不必要的转换差异(一致性很重要;你使用的两种样式中的哪一种并不重要,但不要在同一代码中使用两者),你有2个字节的差异长度。第二个比第一个更可能是正确的。
在第一种情况下,您分配了2个字节太少,并且副本覆盖了malloc()
等依赖的一些控制信息,因此free()
崩溃了,因为它损坏了它管理的内存。 / p>
在这种情况下,问题不是误算strcpy()
。
破坏内存的一个问题是受害者代码(发现问题的代码)通常与导致问题的代码相差甚远。
这个循环:
for(int i=0; i < msgSize; i++)
{
if(currMessage[i] == '\n')
{
trimIndex = i;
break;
}
}
可以替换为:
char *newline = strchr(currMessage, '\n');
if (newline == 0)
...deal with no newline in the current messages...
trimIndex = newline - currMessage;
答案 2 :(得分:0)
在malloc()
来电之前添加此代码:
// we need the destination buffer to be large enough for the '\f' character, plus
// the remaining string, plus the null terminator
printf("Allocating: %d Need: %d\n", (msgSize - trimIndex - 1), 1 + strlen(&currMessage[trimIndex+1]) + 1);
我认为它会告诉你这个问题。
已经一次又一次地证明手计算缓冲区大小可能容易出错。有时您必须这样做,但有时您可以让函数处理那些容易出错的方面:
// e.g.: "\fProgram started\r\nHow are you?\r".
char *trimMessage = strdup( &currMessage[trimIndex]);
if (trimMessage && (trimMessage[0] == '\n')) {
trimMessage[0] = '\f';
}
如果您的运行时没有strdup()
,那么实施(http://snipplr.com/view/16919/strdup/)就足够了。
作为最终修改,这里有一个替代的,简化的trimMessage()
,我相信它是等效的:
void trimMessage()
{
char *newline = strchr(currMessage, '\n');
if (!newline) return;
memmove( currMessage, newline, strlen(newline) + 1);
currMessage[0] = '\f'; // replace '\n'
messageCount--;
}