返回一个malloc指针

11

这是一个相当基础的问题,我对在C语言中传递内存有些不确定。

如果我有以下代码:

CGPoint* tileForCoordinates (CGPoint position, short width, short height)
{   
   CGPoint *tileCoordinate = (CGPoint*)malloc(sizeof(CGPoint));
   tileCoordinate->xTile = (position.xPosition / width);
   tileCoordinate->yTile = (position.yPosition / height);

   return tileCoordinate;
}

如果我想在另一个源文件或其他地方调用它,我需要在上面声明一个指针并返回它吗?如果是这样,在我从另一个类中调用它时,该怎么做?

CGPoint *currentTilePosition = tileForCoordinates(curPosition, 50, 50);

malloc返回的指针需要被释放吗?有什么注意事项? :)


2
在 C 语言中,你不需要对 malloc 的返回值进行强制类型转换。但是有些人(比如我自己)认为这是一个坏习惯,因为这会引起很多争议和问题。 - Chris Lutz
谢谢,知道了。那么这将导致删除从malloc返回的指针以及函数返回的指针? - some_id
4
我认为你可能误解了 - malloc 函数返回一个 void * 指针,可以隐式转换为任何其他指针类型。因此,CGPoint *x = malloc(sizeof(CGPoint)); 就足够了。事实上,我建议使用 CGPoint *x = malloc(sizeof *x);。如果 x 的类型发生变化,第一行代码需要修改两次,如果忘记了,可能会为对象分配过少的空间。在第二行中,当你改变 x 的类型时,sizeof 会自动调整。 - Chris Lutz
在这里的一些答案之后,似乎正确的做法是在某个地方分配内存给这个CGPoint,然后将其传递到该函数中进行操作(删除语句CGPoint tileCoordinate = (CGPoint)malloc(sizeof(CGPoint));并修改参数)。您上面的评论是否适用于原始问题中的函数内malloc?现在有点晚了 :/ - some_id
5个回答

10

回答"malloc()返回的指针发生了什么?"

malloc()声明的指针将成为当前执行函数堆栈帧中的一个值。当堆栈帧(以及*tileCoordinate)在函数返回时消失,该指针也会消失。

然而,因为您将指针值返回给调用函数,它现在存在于当前堆栈帧中(在返回后)。这个值由变量*currentTilePosition引用。

malloc()分配的内存是完全不同的故事;动态分配的内存存在于堆上。您应该在进行分配的同一实现中释放任何您分配的内存。这意味着通常在同一文件中使用free()currentTilePosition进行调用,一旦您完成使用它。


4

你能将tileCoordinate作为函数调用的参数传递吗?这样,调用者会更容易记住malloc/calloc和free。


那么,创建 CGPoint tileCoordinate = (CGPoint)malloc(sizeof(CGPoint)); 然后将其设置为 tileForCoordinates (CGPoint *tileCoord, CGPoint position, short width, short height) 的等号之前?函数返回会是什么样子? - some_id
CGPoint *tileCoordinate = (CGPoint)malloc(sizeof(CGPoint)); // 申请内存空间integer = tileForCoordinates(CGPoint position, short width, short height, &tileCoordinate); // 使用函数,&tileCoordinate为指向CGPoint类型数据的指针变量<work done> // 工作完成 free(tileCoordinate); // 释放内存空间该函数可更改为返回一个表示成功/失败的整数,并将CGPoint *tileCoordinate作为输入/输出参数。 - vpit3833
谢谢。所以,如果传入一个指针并且更改了它的数据,就不需要返回它,这是正确的吗? - some_id
是的,而且调用者可以自由地为指针分配和销毁对象。 - vpit3833
1
@vpit3833: 你应该使用tileCoordinate,而不是&tileCoordinate - 否则,你的参数必须是一个CGPoint**,这是不必要的间接引用。此外,如果tileCoordinate是在局部范围内,你根本不需要使用malloc / free-只需在堆栈上分配它(然后你将使用&tileCoordinate作为参数)。 - Joshua Warner
@Joshua Warner 对的,那是我的打字错误 :D 我的意思是tileCoordinate。 - vpit3833

3
调用者需要使用free函数释放由您的函数malloc分配的指针。
但是,如果您将tileForCoordinates函数打包到动态库中,则还应提供一个相应的函数来释放内存,而不是让调用者这样做。这是因为调用者可能会以与您的库不同的方式链接到C运行时。例如,如果您的库静态链接到C运行时,而调用者动态链接到它,或者反之亦然,让调用者释放内存将导致崩溃。在这种情况下,您可以提供类似以下的函数:
void freeTileCoordinates( CGPoint **tileCoordinate )
{
  // Also add additional checks for NULL pointer
  free( *tileCoordinate );
  *tileCoordinate = NULL;
}

使用示例:

CGPoint *point = tileForCoordinates( ... );

// use point
...

// now free it
freeTileCoordinates( &point );

我知道这个问题有点蠢,但是双星号(**)是什么意思? - some_id
@Helium3:双星号的原因是释放函数可以使用指向由您的函数malloc分配的原始指针的指针。这使得函数在释放内存后可以将该指针设置为NULL。我已添加示例用法。 - Praetorian
2
准确地说,C语言没有真正的按引用传递方式,因此您必须传递指向指针的指针,以便能够修改原始指针。 - Praetorian

3

遵循的非常基本的规则是,如果您返回了一个 *malloc* 的指针,要么给用户一个函数来传递指针进行释放,要么记录下您 *malloc* 了它并且他们应该自己释放。

我见过各种解决方案(包括完全的内存管理抽象),个人更喜欢指定的 API free 方法,因为当需要释放对象时清晰明确,而且它使 API 最后尝试做任何其他清理工作的机会。


谢谢。所以最好使用malloc分配一个结构体,然后通过调用函数来设置它,例如CGPoint *currentTilePosition = tileForCoordinates(curPosition, 50, 50);? - some_id
@Helium,你问了一些东西是否“更好”,但我没有完全听清楚另一个选项是什么。也就是说,我只看到了问题的一面。 - Andrew White
那么我应该使用malloc函数来分配一个结构体,然后通过调用tileForCoordinates(curPosition, 50, 50)函数对其进行设置,对吗?;) - some_id

1
通常情况下,你使用malloc分配的内存,也需要使用free释放。另一个规则是,如果你的API进行了内存分配,那么你的API也应该进行内存释放,因为在将来的某个时间点,你可能会更改底层机制。此外,在Windows中,如果内存由DLL分配,则必须由同一DLL释放,否则会发生崩溃等问题。
因此,一般情况下,模式看起来像这样:
MyType* foo = myapi_dosomething(x,y,z);
if (!foo) die("No Foo!");
use_object(foo);
myapi_free(foo);
foo=NULL; // just in case

关于 DLL 很有趣,但我在一段时间前就放弃了 Windows。:( 我看到过在 Obj C 中使用 foo = nil; 然后 [foo release]; 这种写法,这两个语句的顺序很重要吗? - some_id
@Helium3:在你释放指针后将其设置为NULL是一种安全预防措施,这样如果你尝试再次使用它,你会立即得到一个段错误而不是未定义的行为。我不知道Objective C——我不做苹果。 - tylerl

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