Chr*_*ice 0 c arrays free undefined-behavior
这真的很奇怪......我无法调试它(尝试了大约两个小时,调试器在一段时间后开始变得干扰...).无论如何,我正在尝试做一些非常简单的事情:
Free an array of strings.该数组的形式如下:
char **myStrings.数组元素初始化为:
myString[index] = malloc(strlen(word));
myString[index] = word;
Run Code Online (Sandbox Code Playgroud)
我正在调用这样的函数:
free_memory(myStrings, size); 其中size是数组的长度(我知道这不是问题,我对它进行了广泛的测试,除了这个函数之外的所有东西都在工作).
free_memory 看起来像这样:
void free_memory(char **list, int size) {
for (int i = 0; i < size; i ++) {
free(list[i]);
}
free(list);
}
Run Code Online (Sandbox Code Playgroud)
现在这里出现了奇怪的部分.if (size> strlen(list[i]))然后程序崩溃了.例如,假设我有一个类似于下面的字符串列表:
myStrings[0] = "Some";
myStrings[1] = "random";
myStrings[2] = "strings";
Run Code Online (Sandbox Code Playgroud)
因此这个数组的长度是3.
如果我把它传递给我的free_memory函数,strlen(myStrings[0]) > 3(4> 3),程序崩溃了.
然而,如果我改变myStrings[0]为"So"代替,然后strlen(myStrings[0]) < 3(2 <3 )和程序没有不崩溃.
所以在我看来,free(list[i])实际上char[]是在那个位置,并尝试释放每个角色,我想这是未定义的行为.
我说这个的唯一原因是因为我可以玩第一个元素的大小,myStrings并且只要我喜欢就让程序崩溃,所以我假设这是问题所在.
注意:我确实尝试通过逐步调用函数来调试它free_memory,注意任何奇怪的值等等,但是当我进入free_memory函数时,调试器崩溃,所以我不确定发生了什么.在我进入函数之前,没有什么是不寻常的,然后世界爆炸了.
另注:我也贴源的缩短版的这个程序(不要太长;引擎收录)这里.我在MinGW上编译c99标志.
PS - 我刚想到这个.我确实传递numUniqueWords给自由函数,我知道这实际上并没有释放我分配的整个内存.我已经两种方式称呼它,这不是问题.我离开了它是怎么做的,因为这是我在首先开始工作之后调用它的方式,我需要在该函数中修改我的一些逻辑.
来源,根据要求(现场):
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include "words.h"
int getNumUniqueWords(char text[], int size);
int main(int argc, char* argv[]) {
setvbuf(stdout, NULL, 4, _IONBF); // For Eclipse... stupid bug. --> does NOT affect the program, just the output to console!
int nbr_words;
char text[] = "Some - \"text, a stdin\". We'll have! also repeat? We'll also have a repeat!";
int length = sizeof(text);
nbr_words = getNumUniqueWords(text, length);
return 0;
}
void free_memory(char **list, int size) {
for (int i = 0; i < size; i ++) {
// You can see that printing the values is fine, as long as free is not called.
// When free is called, the program will crash if (size > strlen(list[i]))
//printf("Wanna free value %d w/len of %d: %s\n", i, strlen(list[i]), list[i]);
free(list[i]);
}
free(list);
}
int getNumUniqueWords(char text[], int length) {
int numTotalWords = 0;
char *word;
printf("Length: %d characters\n", length);
char totalWords[length];
strcpy(totalWords, text);
word = strtok(totalWords, " ,.-!?()\"0123456789");
while (word != NULL) {
numTotalWords ++;
printf("%s\n", word);
word = strtok(NULL, " ,.-!?()\"0123456789");
}
printf("Looks like we counted %d total words\n\n", numTotalWords);
char *uniqueWords[numTotalWords];
char *tempWord;
int wordAlreadyExists = 0;
int numUniqueWords = 0;
char totalWordsCopy[length];
strcpy(totalWordsCopy, text);
for (int i = 0; i < numTotalWords; i++) {
uniqueWords[i] = NULL;
}
// Tokenize until all the text is consumed.
word = strtok(totalWordsCopy, " ,.-!?()\"0123456789");
while (word != NULL) {
// Look through the word list for the current token.
for (int j = 0; j < numTotalWords; j ++) {
// Just for clarity, no real meaning.
tempWord = uniqueWords[j];
// The word list is either empty or the current token is not in the list.
if (tempWord == NULL) {
break;
}
//printf("Comparing (%s) with (%s)\n", tempWord, word);
// If the current token is the same as the current element in the word list, mark and break
if (strcmp(tempWord, word) == 0) {
printf("\nDuplicate: (%s)\n\n", word);
wordAlreadyExists = 1;
break;
}
}
// Word does not exist, add it to the array.
if (!wordAlreadyExists) {
uniqueWords[numUniqueWords] = malloc(strlen(word));
uniqueWords[numUniqueWords] = word;
numUniqueWords ++;
printf("Unique: %s\n", word);
}
// Reset flags and continue.
wordAlreadyExists = 0;
word = strtok(NULL, " ,.-!?()\"0123456789");
}
// Print out the array just for funsies - make sure it's working properly.
for (int x = 0; x <numUniqueWords; x++) {
printf("Unique list %d: %s\n", x, uniqueWords[x]);
}
printf("\nNumber of unique words: %d\n\n", numUniqueWords);
// Right below is where things start to suck.
free_memory(uniqueWords, numUniqueWords);
return numUniqueWords;
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 10
你已经得到了这个问题的答案,所以让我回答一个不同的问题:
我有多个容易犯的错误 - 分配一个错误大小的缓冲区并释放非malloc内存.我调试了几个小时,无处可去.我怎么能更有效地度过那段时间?
您可能花了这些时间编写自己的内存分配器,这些内存分配器会自动发现错误.
当我编写大量的C和C++代码时,我为我的程序编写了辅助方法,将所有mallocs和frees转换为不仅仅分配内存的调用.(注意像strdup这样的方法是伪装的malloc.)如果用户要求比如说32个字节,那么我的辅助方法会增加24个字节并实际分配56个字节.(这是在一个有4字节整数和指针的系统上.)我保留了一个静态计数器和一个双向链表的静态头尾.然后我会填写我分配的内存,如下所示:
并返回指向字节20的指针.
自由代码将传入指针并减去四,并验证字节16-19仍然是01 23 45 67.如果它们不是那么你要么释放一个你没有用这个分配器分配的块,或者你已经以某种方式写在指针之前.无论哪种方式,它都会断言.
如果该检查成功,那么它将再返回四个并读取大小.现在我们知道块的结尾在哪里,我们可以验证字节52到55仍然是89 AB CD EF.如果他们不是那么你在某个地方写一个块的末尾.再次断言.
现在我们知道块没有损坏,我们将它从链表中删除,将块的所有内存设置为CC CC CC CC ...并释放块.我们使用CC,因为这是x86上的"中断调试器"指令.如果以某种方式我们最终得到指向这样一个块的指令指针,如果它破坏了它是好的!
如果有问题,那么您也知道它是哪个分配,因为您在块中有分配计数.
现在我们有一个系统可以找到你的错误.在产品的发布版本中,只需将其关闭,以便分配器正常调用malloc.
此外,您可以使用此系统来查找其他错误.例如,如果您认为某个地方有内存泄漏,那么您需要查看链接列表; 你有一份完整的所有未完成分配清单,可以找出哪些是不必要的.如果你认为你为给定的块分配了太多的内存,那么你可以检查你的免费代码,看看块中是否有很多33个即将被释放; 这表明你分配的块太大了.等等.
最后:这只是一个起点.当我专业地使用这个调试分配器时,我将它扩展为线程安全,以便它可以告诉我哪种分配器正在进行分配(malloc,strdup,new,IMalloc等),是否存在不匹配alloc和free函数,包含分配的源文件,分配时调用堆栈的内容,平均,最小和最大块大小是什么,子系统负责什么内存使用...
C要求你管理自己的记忆; 这绝对有其优点和缺点.我的观点是,利弊超过了职业选手; 我更喜欢使用自动存储语言.但是,必须管理自己的存储的好处是,您可以自由地构建满足您需求的存储管理系统,其中包括您的调试需求.如果您必须使用需要您管理存储的语言,请使用该功能,并构建一个非常强大的子系统,您可以使用它来解决专业级问题.