-7
void str_cpy(char **des,char* src){
   if(*des || *des==src)
     free(*des);
   *des = (char*)malloc(sizeof(char)*(strlen(src)+1));
   if(!*des)
     return ;
   memcpy ( *des, src, (strlen(src)+1)*sizeof(char) );
 }

为什么 line2 中的 free 让 memcpy 什么都不做?malloc之前的free可以吗??

4

4 回答 4

4

不!!!:-) 在尝试使用之前释放(src)是不行的!(在 *dest == src 的情况下,代码的第 2 行,即 free(*dest) 本质上与 free(src) 相同,然后您使用 src,从那时起它不指向正确分配的内存堵塞。)

尝试使用释放的内存会导致未定义的行为。当某些事情未定义时,任何事情都会发生!也许 free 已经将 src 中的第一个字节更改为 0(在这种情况下 strlen(src) == 0)或者 memcpy 检测到源内存没有正确分配或者可能发生了其他事情。不值得追究为什么会出现这种行为的问题。如果您更改构建设置(调试/优化等)或在不同的系统上构建您的程序(实际上是库,而不是您使用的编译器在这里有所不同),您可能会得到不同的结果。

无论如何,当 *dest == src 时,你不需要 free、strlen、malloc 和复制任何东西;你可以回来。

void str_cpy(char **des,char* src){
   if(*des==src) return;
   ...
 }

通常,使用您的函数的人会期望您不要对第二个参数 src 做任何有趣的事情,例如更改其内容或释放其内存。我还将参数类型更改为 const char * src。虽然这不会阻止您释放 src 内存块,但它会使函数的预期用途更加清晰。

实际上,函数签名还有另一个更大的问题。*dest 可能指向之前在堆中分配的内存(唯一可以使用 free(*dest) 的情况,当然假设 *dest != src)或之前分配的堆栈内存(在这种情况下 free(*dest) ) 不行),但也有可能它指向静态分配的内存(字符串文字)或不指向分配的内存周期。

标准库函数通过不使用此类签名(即使用 char ** 类型的参数)来避免这个问题。

与您正在做的最相关的功能的签名是:

 char * strcpy(char *restrict s1, const char *restrict s2);

 char * strncpy(char *restrict s1, const char *restrict s2, size_t n);

 char * strdup(const char *s1);

更多信息请访问: http: //www.manpagez.com/man/3/strncpy/http://www.manpagez.com/man/3/strncpy/

这些函数都不必担心内存分配的位置。这三个都要求它们的参数是有效的指针(指向静态或动态分配的(堆/堆栈)内存。我怀疑 strdup 实际上最适合您正在做的事情(您似乎想为重复的字符串分配新内存) . strcpy 和 strncpy 不分配新内存。(有关详细信息,请参阅上面的文档链接。)

你看到那里的模式了吗?strdup 返回一个指向新分配内存的指针(与 malloc 完全一样),并且 strcpy 和 strcpy 采用 char * 参数并且不分配内存。这些函数都不必处理您的函数所存在的问题,即确定(在 C 中不可能) *dest 指向的内存类型。Kernighan 和 Ritchie 选择这样做是有原因的 :-)

您可以提供明确说明 *dest 必须指向堆中先前分配的内存块或等于 NULL 的文档。然而,优秀的库设计者不会这样做,因为它将责任转嫁给用户(因为用户犯错误)。

依赖文档(即让库设计者将责任传递给其用户)是一条危险的道路。最好完全避免这个问题。好的 C 代码从函数签名开始 :-)

最后一点:您在代码中使用了 strlen() 两次。出于性能原因,最好只使用一次并将其返回值存储在局部变量中,然后在当前调用 strlen() 的地方使用该变量。

于 2012-11-27T13:31:40.287 回答
3

在 C 中,您只能释放由malloccalloc或分配的内存realloc。所以释放除此之外的内存是Undefined behaviour

在你的情况下,不清楚是什么*des。以及是否由malloc,calloc或分配realloc

或者你在调用环境时已经这样做了

char des[20]= "hello , world \n";

或者可能

char * des= "hello . world \n";
于 2012-11-27T13:25:36.637 回答
1

正如评论中向您指出的那样,您的函数需要文档。如果输入正确,它将完全按照您的要求工作:

这有效:

char * str1 = "hello";
char * str2 = malloc(6);   // Of course this is pointless since you'll free it anyway
str_cpy(&str2, str1);

这样做也是如此:

char * str1 = "hello";
char * str2 = NULL;
str_cpy(&str2, str1);

但是这个:

char * str1 = "hello";
char * str2;
str_cpy(&str2, str1);

或这个:

char * str1 = "hello";
char str2[6];
str_cpy(&str2, str1);

或这个:

char * str1 = "hello";
char * str2 = str1;
str_cpy(&str2, str1);

会崩溃和燃烧。您需要在评论中注明您的功能的所有前/后条件。

于 2012-11-27T13:35:17.863 回答
0
void str_cpy(char **des,char* src){
   if(*des || *des==src)
     free(*des);
   *des = (char*)malloc(sizeof(char)*(strlen(src)+1));
   if(!*des)
     return ;
   memcpy ( *des, src, (strlen(src)+1)*sizeof(char) );
 }
  • 在 malloc 之前释放不是必需的,它可能包含一些垃圾值,并且您正在释放导致的垃圾(核心转储)。
  • 如果 ( *des == src ) 并且你释放了 des,这意味着 'src' 也被释放了,下面你在 memcpy 中使用 src。

正确做法:

void str_cpy(char **des,char* src){
   *des = (char*)malloc(sizeof(char)*(strlen(src)+1));
   if(!*des)
     return ;
   memcpy ( *des, src, (strlen(src)+1)*sizeof(char) );
 }
于 2012-11-27T13:44:55.007 回答