如何在C语言中清除传递给函数的char*?

3

如何编写一个函数来清空C字符串并/或释放已分配的内存?为什么下面的代码不起作用?

void freeStr(char** str) {
    free(*str);
}
int main() {
    char* str = "some string";
    freeStr(&str);
    printf("%s", str);
    return 0;
}

7
你只能在先使用过malloc()分配内存的指针上使用free()。 - Ondřej Čertík
1
@ groove 话虽如此,指针值应该足够了...不需要指向指针的指针。由于它是const char [],因此您无法安全地修改字符串文字的数据,也不应该free它,因为您没有malloc它...它甚至不在堆上。 - obataku
请随意给您认为有帮助的答案点赞或接受。 - Chimera
@oldrinb 你说,我在函数freeStr()中应该使用free(str)而不是free(*str),对吗? - groove
5个回答

10

你既不能释放也不能清除字符串字面量所代表的数组的内存。

然而,要失去对其的访问权限,您只需要更改存储其地址的指针:

str = NULL; // in main
// or
*str = NULL; // in freeStr

4
您分配的字符串位于CONST_DATA部分(无法修改),因此您不能对其调用free。
该内存部分是在编译时分配的,是只读的。该内存部分仍然包含"some string\0" CONST_DATA部分在汇编中类似于数据段(DS),其中包含全局和静态(只读)变量,它们在编译时初始化。它们在运行时不会改变,并继续为运行中的程序分配。

2
嗯,你最后一段话是错的。程序不会覆盖其他程序的内存。此外,OP实际上并没有释放内存。 - user703016
1
@Cicada他说“可能”。我同意他的观点,即操作系统可以将任何释放的物理地址范围分配给其他程序。 - Johannes Schaub - litb
1
是的,但它将不再映射到您的地址空间中,也无法被读取或写入。 - Puppy
1
只要用户有指向它的指针,它仍然可以被读取。由于它已经在“CONST_DATA”部分中了,因此无法对其进行写入操作。 - Anirudh Ramanathan
@DarkCthulhu,“那部分内存在编译时被分配,并且是只读的。”您能否详细说明一下,这将有助于我更好地理解编译时的内存分配?据我所知,在编译时我们只会创建一些虚拟地址并将变量绑定到该虚拟地址(堆栈内存?),我们将在程序运行时获得原始内存?那么这个只读内存在哪里,这个“只读内存变量”属于内存布局的哪个部分?请解释一下。 - uss

4
您可能会发现阅读C FAQ - malloc section有所帮助。
正如其他人指出的那样,您无法释放或修改字符串文字,因为它们通常放置在只读内存区段中。但是,如果您想要清除并free()动态分配的内存,则可以执行以下操作:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LENGTH  20

void freeStr(char **str)
{
    free( *str );
    *str = NULL;   
}

int main() {

    char *str = malloc( sizeof(char) * LENGTH);

    // clear that allocated memory
    memset( str, 0x00, LENGTH );

    // Put some text into the memory
    strncpy( str, "Some text", LENGTH-1 );

    printf("Before: %s\n", str);


    freeStr(&str);
    // string is now NULL and the memory is free.
    printf("After: %s\n", str);

    return 0;
}

1
这里必须使用memset()吗?(我感到困惑,因为我们在清除内存后立即将一些文本放入其中)。 - groove
1
不需要。我把它放在那里只是为了展示如何清除已分配的内存。 - Chimera
1
@Chimera,为什么你需要使用 *str = NULL;?为什么仅使用 free(*str); 不能清除内存(因为我们正在释放内存)?为什么我们能够获取已经被释放或删除的字符串内存内容?你能详细解释一下吗? - uss

3
答案都比较好,但是有一些细节他们忽略了或省略了。您的问题也稍微有些模糊。
如果您想要清除C字符串,您需要将它指向的内存空间用空字节覆盖。您只能通过堆分配器调用堆分配器函数(例如malloc()calloc()realloc()等)来free()一个C字符串。
所以如果您使用malloc()或其他堆分配函数来分配字符串,并将一个字符串复制到其中,您可以通过调用memset()来清除它,但您仍然需要调用free()将该内存释放回堆中。
例如:
char* strPtr = malloc(32); // allocate 32 bytes of storage off the heap
strcpy(strPtr, "test");    // copy a string into the newly allocated block on the heap
memset(strPtr, 0, 32);     // clear the allocated block
free(strPtr);              // return the allocated block to the heap

此外,请注意C语言使用块级作用域,您无需显式释放使用默认存储类(auto)声明的数组,因为它们在该块超出作用域时自动从堆栈帧中弹出。
void foo() {
    char strArray[32];          // this is a stack allocation
    strcpy(strArray, "test");   // copy a string onto the stack
    return;                     // no call to free() required
}

最后,另一种分配方法是静态的。当您使用声明时: ```c++ int array[10]; ``` 数组会被分配在程序的数据部分,这意味着它在程序执行期间始终占用相同的内存空间。
char* str = "literal string";

字符串“literal string”的空间位于静态分配的段中,不应该被写入。在某些平台上,如果尝试写入该内存段,则会导致段错误。不应尝试清除静态分配的内存块,因为无法保证它们映射到的段是可写的。


我在使用free()之前必须使用memset()吗?没有使用memset()而使用free()会导致内存泄漏吗? - groove
1
不需要。memset() 只会用 null 字节覆盖缓冲区的内容。堆分配器需要释放那块内存回到堆中的唯一输入是地址。请注意,分配器在将指针返回给您的代码之前不会自动清零缓冲区。这种情况会产生各种有趣的行为 :)。 - Max DeLiso

2

http://en.cppreference.com/w/c/memory/free

void free( void* ptr );

释放之前由malloc()、calloc()或realloc()分配的空间。如果ptr是空指针,则函数不执行任何操作。

您的字符串没有使用这些函数中的任何一个进行分配。我认为是这样的。

void freeStr(char **str) {
    *str = NULL;
}
int main() {
    char* str = (char *)"some string";
    printf("Before: %s\n", str);
    freeStr(&str);
    printf("After: %s\n", str);
    return 0;
}

1
这实际上对原始指针没有任何影响。指针是按值传递的。 - chris
忘记了参考,谢谢 Chris。哇 Griwes,非常感谢你提供的信息。 - Jorge Fuentes González
1
如果你没有使用new,就不能使用delete;如果你没有使用malloccallocrealloc,就不能使用free - SSpoke
SSpoke,我没有使用delete或free,只是使指针失效。 - Jorge Fuentes González
1
那段代码是错误的。注意C标签。这是正确的将指针置为NULL的代码。http://pastebin.com/YxrP4SKZ - Chimera
我已经编译了它,而且它可以工作。顺便说一下,我将编辑它,谢谢 Chimera。 - Jorge Fuentes González

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接