我刚开始为uni工作,它为我提出了一个问题。
我不明白如何在没有内存泄漏的情况下从函数返回字符串。
char* trim(char* line) {
int start = 0;
int end = strlen(line) - 1;
/* find the start position of the string */
while(isspace(line[start]) != 0) {
start++;
}
//printf("start is %d\n", start);
/* find the position end of the string */
while(isspace(line[end]) != 0) {
end--;
}
//printf("end is %d\n", end);
/* calculate string length and add 1 for the sentinel */
int len = end - start + 2;
/* initialise char array to len and read in characters */
int i;
char* trimmed = calloc(sizeof(char), len);
for(i = 0; i < (len - 1); i++) {
trimmed[i] = line[start + i];
}
trimmed[len - 1] = '\0';
return trimmed;
}
你可以看到我正在返回一个指向char的指针,这是一个数组。我发现如果我试图通过以下方式制作'修剪'数组:
char trimmed[len];
然后编译器会抛出一条消息,说明在这一行上预期有一个常量。我认为这意味着由于某种原因,在初始化数组时你不能使用变量作为数组长度,尽管有些东西告诉我这不可能是正确的。
因此,我通过为char指针分配一些内存来创建我的数组。
据我所知,这个功能对于它想要做的事情来说可能是次要的,但我真正想知道的是:
您通常可以使用变量初始化数组来声明长度,如:
char trimmed [len];
如果我有一个属于该类型的数组(char trimmed []),它将具有与char(即char *)指针相同的返回类型。
如果我通过分配一些内存并将其分配给char指针来创建我的数组,我该如何释放这个内存。在我看来,一旦我返回这个数组,我就无法访问它来释放它,因为它是一个局部变量。
答案 0 :(得分:15)
要解决(3) - 完成后,您可以free
调用代码中新分配的字符串:
char* tmp = trim(myline);
if (tmp != NULL) {
....
free( tmp );
}
但是这会给调用者带来负担,以便记住释放内存。因此,您可以考虑将分配的缓冲区和缓冲区大小传递给trim()
,例如:
void trim(char* line, char *trimmed_buf, int trimmed_buf_len){ ... }
Neil在解决您的其他问题方面表现出色。基本上数组声明char trimmed[len];
将在堆栈上声明一个局部变量,所以虽然在语法上将char *
返回到此内存是正确的,但它指向的内存位置将不再有效。
答案 1 :(得分:6)
要回答您的具体问题,请使用以下语法:
char trimmed[len];
其中len
是变量,只允许在C99中,而不是在C89或C ++中。返回类型确实是char *
,但返回局部变量trimmed
会导致未定义的行为,所以不要这样做。如果你通过calloc
在一个函数中动态分配一个数组并返回它,那么使用该函数返回的指针,由函数的调用者来释放它。
答案 2 :(得分:1)
关于动态调整像char trimmed[len];
这样的数组声明,最新版本的C标准(ISO/IEC 9899:1999)允许这样做,但是对于这个函数它根本没有帮助。 trimmed
变量的范围在trim
函数中,并且它在堆栈上分配。因此,如果在代码中放置return trimmed;
,则返回指向堆栈变量的指针,此变量所在的堆栈部分将在函数返回时释放,这样就无法解决好吧......
答案 3 :(得分:1)
让调用者传入指向函数的内存区域(最大长度)的指针。这样调用者将负责分配(和释放)内存,并且该函数仅提供约束传递给函数的缓冲区的输出。
当然,使用该功能的人仍然可以解决问题(通过指定与实际缓冲区大小无关的长度,但是你可以正确地认为它是调用者的错误,而不是这个函数错误。
答案 4 :(得分:0)
除了使用std::string
之类的C ++解决方案之外,您总是可以分配一个设置大小的数组,并将大小作为参数传递,并将数组作为参数传递或作为指针?
这样,数据在同一范围内分配,没有内存泄漏。
然后你可以在打电话后随时释放内存。这意味着负责创建它的代码和负责销毁数据的代码不一样,这可能是错误和错误的前兆。
答案 5 :(得分:0)
在函数外部分配和删除:
使用char trimmed [SIZE]从堆栈中分配它并将其传递给函数或使用calloc传递给堆,并将其传递给函数。
在函数内部进行分配并在外部删除:
您使用calloc在函数内部分配它,但调用者必须使用free释放它。
答案 6 :(得分:0)
char * line
内容。这有效,因为trim()总是只删除东西。我认为传入的缓冲区可以更频繁地工作,并且养成习惯是一件好事。至于你的函数,考虑使用memcpy()或其表兄弟将字节复制到char缓冲区中和从char缓冲区中复制出来。