假设我有以下C代码:
#include <stdio.h>
#include <stdlib.h>
#define NUM_PEOPLE 24
typedef struct {
char **name;
int age;
} person_t;
void get_person_info(person_t *person);
int main(int argc, char **argv) {
for (int i = 0; i < NUM_PEOPLE; i++) {
person_t new_person;
get_person_info(&new_person);
}
return 0;
}
其中get_person_info()
只填写传入指针的person_t
结构。malloc()
内new_person
的{{1}}内存是否必要?也就是说,该行
main()
而不是
person_t new_person;
然后更改person_t *new_person = (person_t *) malloc(sizeof(person_t));
接受get_person_info()
而不是person_t **
?
很抱歉,如果这个问题令人困惑 - 我不确定这是否是需要保留内存的情况,因为指向该内存的指针被传递到person_t *
以避免导致分段错误。
答案 0 :(得分:2)
两者都是正确的,这取决于您要使用person_info
的位置。
在堆栈上分配:
for (int i = 0; i < NUM_PEOPLE; i++) {
person_t new_person;
get_person_info(&new_person);
}
在堆栈上创建一个person_t
对象并使用数据填充new_person
对象,因为循环只会这样做,对象在下一次循环迭代时超出范围并且数据丢失。
使用malloc:
for (int i = 0; i < NUM_PEOPLE; i++) {
person_t *new_person = malloc(sizeof(person_t));
get_person_info(new_person);
}
在堆上创建一个person_t
对象并用数据填充它,因为它在堆上分配的new_person
对象将比循环范围更长,这当前意味着你正在泄漏内存,因为你有没有指针指向前一个循环周期的person_t
对象的数据。
答案 1 :(得分:1)
两种方式都是正确的!
void get_person_infperson_t *person);
然后更改get_person_info()以接受person_t **而不是person_t *?
您无需更改功能参数 - main
。只需将指针传递给get_person_info(new_person);
,就像这样 -
new_person
但是在以前没有分配内存的情况下,你将无法在定义的块之外使用它,而如果你的程序依赖于它的生命,你可以在堆上为它分配内存。
在您的代码中,loop
仅在loop
内使用,因此如果您不打算使用外部循环,则可能不需要动态分配。
但是如果你想在free
之外使用它,你也应该使用动态分配。但不要忘记public static event DownEventHandler Down;
public static delegate void DownEventHandler(string Key);
Down(FunctionChar((Keys)lParam.Code)); // lParam.code is an integer value.
private string FunctionChar(Keys e)
{
if(e >=65 && e<=90){
if (Control.IsKeyLocked(Keys.CapsLock) || ((Control.ModifierKeys !=0) && (Keys.Shift) != 0))
{
return e.ToString;
}
else
{
return e.ToString.ToLower;
}
它。
答案 2 :(得分:1)
不确定结构的
malloc
内存是否存在?
简短的回答是:无需在您的情况下执行。如果你想在for
循环之外使用你的对象,你可以通过动态分配的内存来实现它,即:
person_t *new_person = malloc(sizeof(person_t));
然后用:
调用它 get_person_info(new_person);
在您的示例中,对象在循环中使用,因此无需执行此操作。
当你使用动态分配的内存时,你应该总是释放它,最后避免内存泄漏。
正如@Johann Gerell所指出的,在删除了返回类型malloc的转换的冗余之后,在C中,分配看起来像:
person_t *new_person = malloc(sizeof(person_t));
malloc
返回一个void指针(void *
),表示它是指向未知数据类型区域的指针。由于强类型系统,在C ++中需要使用强制转换,而在C中则不是这样。
答案 3 :(得分:1)
您的困惑源于不了解对象存储持续时间和指针。让我们分别看一下,以便清楚一点。
对象可以具有自动或动态存储持续时间。
自动,顾名思义,将由编译器为您管理。您只需定义一个变量,使用它,当它超出范围时,对象会自动为您销毁。一个简单的例子:
if (flag) {
int i = 0;
/* some calc. involving i */
}
// i is dead here; it cannot be accessed and its storage is reclaimed
当控件进入if
的范围时,将自动分配足以容纳int
的内存,并为其分配值0
。一旦您使用i
结束,当控件退出作用域时,名称i
超出范围,因此程序将无法再访问它,并且它将自动为您分配存储区域被收回。
让我们假设您想要动态分配对象,即您希望管理存储,从而管理对象的生命周期,而不会使用作用域或编译器,然后您将继续从平台请求存储空间使用malloc
malloc(sizeof(int));
请注意,我们并未将malloc
的返回值分配给您以前看到的任何指针。我们将稍微指出一下,让我们现在完成动态对象。在此,int
会将大到足以容纳malloc
的空间移交给您。当你完成它时,由free
来决定你。因此,这个未命名的int
对象的生命周期在你手中,并且超出了创建它的代码的范围。只有在您明确调用free
时才会结束。如果没有匹配的free
来电,您就会有臭名昭着的memory leak。
指针就是它的名字所说的 - 一个可以引用另一个对象的对象。指针永远不会指向它( pointee )。指针是一个对象,它的指针是另一个独立的独立对象。您可以将指针指向另一个命名对象,未命名对象或任何内容(NULL
)。
int i = 0;
int *ptr1 = &i; // ptr1 points to the automatic int object i
int *ptr2 = malloc(sizeof(int)); // ptr2 points to some unnamed int object
int *ptr3 = NULL; // ptr3 points to nothing
因此,大多数人混淆动态分配的指针的指针的原因来自于:指针,这里没有名称,因此它们总是通过指针引用;有些人误认为是另一个。
这里采用指针的功能是合适的,因为从调用者的角度来看,它是一个灵活的功能:它可以同时采用自动和动态对象。我可以创建一个自动变量并将其传入,或者我也可以传递一个动态变量:
void get_person_info(person_t *person);
person_t o { };
get_person_info(&a);
person_t *p = malloc(sizeof(person_t));
get_person_info(p);
free(p);
malloc()
是否需要new_person within main()
内存?
没有。您可以定义自动变量并将其传递给函数。实际上,建议您尝试最小化动态对象的使用并优先选择自动对象
free
到malloc
,从而导致内存泄漏。但是,自动变量通常在堆栈中分配,因此可以在堆栈上创建的数量和大小的上限相对低于动态分配的值(通常来自堆)。
更改
get_person_info()
接受person_t **
而不是person_t *
?
不,如果你这样做,那么传递自动变量的选项仍然是可能的,但是很麻烦:
void foo(int **o);
int i = 0;
int *p = &i; // p is redundant
foo(&p);
int *p = malloc(sizeof(int));
foo(&p);
与简单的
相反void bar(int *o);
int i = 0;
bar(&i);
int *p = malloc(sizeof(int));
bar(p);