2010-07-20 917 views
4

当将char *作为参数传递给函数时,被调用的函数是否应该对该字符串进行自由处理?否则,数据会“丢失”,程序会泄漏数据。或者,编译器以特殊的方式处理char *,以避免每个人都必须一直空闲,并自动删除它,使其超出范围?我将“字符串”传递给该函数,因此不会将实例传递给已存在的char *。或者应该使用char []来代替?只是觉得非常愚蠢的是为参数输入设置一个固定的限制。char *作为函数的参数C

+1

迈克尔的良好答案告诉你考虑将字符串存储在所谓的“静态存储器”中 - 这是存储器在程序映像加载时由操作系统初始化的存储器。 Naveen的例子和free/malloc自己使用“免费商店”,有时称为“堆”。还有“自动存储”通常作为“堆栈”来实现。其他存储可能性是操作系统映射的ABI内存区域,内存映射文件等。一个例子可能是操作系统调用返回当前目录字符串 - 这个“char *”可能指向OS的每线程数据结构。 – 2010-07-20 06:39:32

+0

您通常不应该为参数输入设置固定限制。相反,让调用者给你一个指针,让*他们*告诉你*字符串是多长。 – bta 2010-07-20 16:47:22

回答

15

请牢记这一简单原则:“始终释放内存在您分配的相同级别”。换句话说,函数不应该尝试释放它本身没有分配的内存。一个简短的例子来阐明这一点:

#include "graphics.h" 

// The graphics API will get a Canvas object for us. This may be newly allocated 
// or one from a pool of pre-allocated objects. 
Canvas* canvas = graphics_get_canvas(); 

// If draw_image() frees canvas, that violates the above principle. 
// The behavior of the program will be unspecified. So, just draw the image 
// and return. 
draw_image (canvas); 

// This is also a violation. 
// free (canvas) ; 

// The right thing to do is to give back the Canvas object to the graphics API 
// so that it is freed at the same 'level' where it was allocated. 
graphics_return_canvas (canvas); 

注意,该函数的名称不是graphics_free_canvas()之类的东西,因为API可以选择释放它或将其返回到池重用。关键是,假定我们没有创建资源的所有权,这是一个非常糟糕的编程实践,除非我们另有具体说明。

+4

这是如何与malloc()和strcmp()的喜欢?他们打算在不同的层面上被释放。事实上,free()本身不会释放内存:-) – paxdiablo 2010-07-20 06:38:42

+0

@paxdiablo,strcmp()?你是说strdup()?如果是这样,我不明白他们是如何在不同的层面上获得释放的。 strcmp()当然不会分配任何东西。 – 2010-07-20 06:41:41

+0

@HH,我不认为他真的是这个意思。答案中概述的原则(清理你自己的混乱)是健全的。避免我以前的一位同事使用的构造函数,他的函数返回一个char *给(文本)答案,在这种情况下,您必须释放它,或者返回错误消息,在这种情况下,您不得释放它。 – 2010-07-20 06:55:35

7

这听起来像你问这个用法:

void foo(char* str); 
foo("test string"); 

这是一个特殊情况; "test string"是存储在可执行文件中的字符串表中的常量字符串,不需要释放。 foo实际上应该采取const char*来说明,并允许字符串文字存储在非恒定char* S IN C++

+0

为了扩大这一点,字符串文字“测试字符串”以* static extent *的char(C++中的const char)的12元素数组的形式存在,这意味着它的内存在程序启动时分配并保存到程序退出。当您将字符串文字作为函数参数传递时,编译器**不会**创建​​字符串的新实例;相反,它传递一个指向现有实例的指针。 – 2010-07-20 18:36:10

7

已被弃用,软键功能是否应该做一个free与否取决于谁拥有的字符串。此代码是完全有效的,不会导致任何内存泄漏:

int main() 
{ 
    char* s = malloc(.....); 
    f(s); 
    free(s); 
} 

free可以内部功能f,如果它需要字符串的所有权进行为好。但是请注意,由于您假定传递给函数f的字符串始终使用malloc或相关函数在堆上进行分配,因此这很危险。如果用户将指针传递给堆栈中分配的字符串,则程序将表现出不可预测的行为。

一般情况下,编译器不会对字符串的内存管理进行任何特殊处理。从编译器的角度来看,它只是一堆字符。

+1

不只是一个堆栈分配的字符串。如果你传递'f()'字符串,并且'f()'试图释放它,你也会遇到麻烦。 – ptomato 2010-07-20 08:58:47

0

有时API需要一个分配的缓冲区,并且它的功能所有者可以调用该API释放它。

myFunc() 
{ 
char *error = malloc(<max size of error string>); 
foo(error); 
//Free the pointer here 
free(error); 

} 

像GLIB一些API API的预期指针的声明的变量

myFunc() 
{ 
GError *error; 

glib_api(&error); 
if (error) 
{ 
printf("Error %s", error-> message); 
// can use glib API to free if error is NON NULL but message is allocated by GLIB API 
g_error_free(error); 
} 
} 

所以,即使你还没有分配的内存,你需要做的释放,而使用标准库的变量地址。

分配的一块内存(如果未释放)会导致多进程环境中的内存较小,从而降低系统的性能。

1

看来你已经习惯了OOP风格。我不喜欢OOP,对我来说,如果我在分配后获得一个对象的副本,会很奇怪。在这种情况下,字符串在内存中的某处,其地址以char *形式发送,而不是整个字符串。 另外,请注意,您可以释放()只有malloc()返回的指针,并且只有一次。

0

用普通的char *,我会建议总是编写一个策略的代码,调用者“拥有”字符串,并负责释放它,如果它是由malloc获得的。另一方面,当然可以设想C中的“伪值传递”字符串对象,作为结构实现,其中策略规定在传递字符串时必须放弃字符串的所有权(或者先复制并传递重复)作为论据。如果这个实现使用引用计数的存储来传递对象只是对存储的引用,那么“重复”操作仅仅是一个引用计数增量加上简单的包装器结构分配(或即使通过值结构)。

0

指向char作为函数变量的指针是指向相同变量的地址,除非它已被替换为常量字符串。你的问题不能用简单的是/否指导来解释;它取决于上下文。在下面的代码中,分别在堆和堆栈上分配的结构通过引用传递以及字符串char *,并将数据插入到结构中。请注意malloc在使用时的不同,但函数的工作原理完全相同。

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

// struct with all data within 
typedef struct d 
{ 
int number; 
char name[50]; 
}data; 
// struct with dynamic char * 
typedef struct d2 
{ 
int number; 
char *name; 
}dynamic_data; 

// generic function placing data into struct 
void InsertData (data * out, int a, char * b) 
{ 
    out->number = a; 
    strcpy(out->name, b); 
} 

// generic function placing data into second struct 
void InsertData2 (dynamic_data * out, int a, char * b) 
{ 
    out->number = a; 
    strcpy(out->name, b); 
} 


int main (void) 
{ 
    char * text = "some string\0"; 
    int n = 20; 
    // allocated struct 
    data stuff; 

    dynamic_data stuff2; 

    dynamic_data * stuff3; 
    // need to allocate pointer within struct only 
    stuff2.name = (char *) malloc(50 * sizeof(char)); 

    // heap allocated struct 
    stuff3 = (dynamic_data *) malloc(50 * sizeof(dynamic_data)); 
    // heap allocated sub element char * 
    stuff3->name = (char *) malloc(50 * sizeof(char)); 


    // this is the data 
    printf ("Pre insertion data\n"); 
    printf ("s=[%s]\n", text); 
    printf ("n=%d\n", n); 

    // this is the function insertting 
    InsertData (&stuff, n, text); 
    printf ("Post insertion data\n"); 
    printf ("stuff.name=[%s]\n", stuff.name); 
    printf ("stuff.number=%d\n", stuff.number); 

    // this is the function inserting 
    InsertData2 (&stuff2, n, text); 
    printf ("Post insertion data\n"); 
    printf ("stuff.name=[%s]\n", stuff2.name); 
    printf ("stuff.number=%d\n", stuff2.number);  

// 
// This is the segfault version - if nothing was allocated for pointers into 
// this function scope, it would crash 


    // this is the function insertting under a heap allocated 
    InsertData2 (stuff3, n, text); 
    printf ("Post insertion data - dynamic version\n"); 
    printf ("stuff3->name=[%s]\n", stuff3->name); 
    printf ("stuff3->number=%d\n", stuff3->number); 

    // free in reverse order 
    free(stuff3->name); 
    free(stuff3); 
    free(stuff2.name); 
    return 0; 
}