从 C 函数返回指针是好还是坏设计? [关闭]
Posted
技术标签:
【中文标题】从 C 函数返回指针是好还是坏设计? [关闭]【英文标题】:return pointer from a C function is good/bad design? [closed] 【发布时间】:2015-08-17 19:39:47 【问题描述】:我想知道 C 函数的返回指针是好还是坏设计?如果这是一种不好的做法,那么以下示例中的好做法是什么:
问题是以下内容的延续部分: c function return static variable
在data.h
文件中:
#include <stdio.h>
#include <stdlib.h>
typedef struct
int age;
int number;
person;
person * getPersonInfo();
在data.c
#include "data.h"
static struct person* person_p = NULL;
person * getPersonInfo()
person_p = (struct person*)malloc(10 * sizeof(struct person));
return person_p;
在main.c
#include "data.h"
int main()
person* pointer = getPersonInfo();
return 0;
基本上main
文件中的main
函数需要获取静态指针person_p
所指向的数组的所有元素的值,如果这不是一个好的做法,那么好的做法应该是什么是吗?
【问题讨论】:
如果你有一个执行malloc
的函数,你也应该有一个匹配的函数来执行free()
,即使它只是一个包装器。在您的 main
中调用 free(pointer)
是不好的设计 - 您在示例中没有这样做,因此会泄漏内存......有点。
标准警告:请do not castmalloc()
和C
中的家人的返回值。
malloc
返回一个指针,是标准库的重要组成部分。你怎么看?
如果没有它,你将如何在 C 中的任何重要程序中相处??
接近投票者:说真的,如果人们认为好的/坏的程序设计是“基于意见”或“艺术”,那么你需要走出车库,接受一些适当的编程教育。什么是好的和坏的程序设计不是基于意见的,关于这个主题有大量的研究,并且全世界的程序员之间都有很多共识。如果人们不能在 SO 上提出程序设计问题,那我不知道这里能问什么。
【参考方案1】:
它不好的唯一原因是它背后没有任何内存管理结构。在您当前的代码中,您有内存泄漏,因为您通过 malloc() 分配了一个 person
结构但没有释放它。
考虑编写一个包装函数来为您处理内存管理,如下所示:
void freePerson(struct person * personToDelete)
free(personToDelete);
然后在你的主要:
int main()
person* pointer = getPersonInfo();
freePerson(pointer); // After you are done using it
return 0;
我还必须警告不要投射malloc()
的结果。根据我的经验,它可能会导致未定义的行为。
【讨论】:
包装器是什么样子的,我不知道 不一定是坏事——它只是将释放分配的内存的责任转移给调用者 这是一个糟糕的设计,因为不应假定调用者释放这些结构。 @IshayPeled 除非您正在编写自己的内存分配例程或类似的程序,否则这是糟糕的设计,也是所有发布的软件中内存泄漏的最常见原因之一过去几十年。【参考方案2】:返回指向私有变量的指针是不好的做法。此外,在当前设计下,您的 .c 文件只能包含一个 person 对象实例。
几乎不用说,动态分配对象的代码也应该负责释放它。根据设计,为了让程序中的其他模块清理混乱而编写的代码总是以内存泄漏而告终。
如果您正在编写至少有些复杂的数据类型,您需要限制对私有变量的访问,拥有结构的多个实例等,那么使用“不透明类型”是一个好习惯。示例(未测试):
// 数据.h
#ifndef DATA_H
#define DATA_H
#include <stdio.h>
#include <stdlib.h>
typedef struct person person; // opaque type
person* person_create (void);
void person_delete (person* p);
void person_set_age (person* p, int age);
int person_get_age (const person* p);
// ... and so on, setters/getters
#endif // DATA_H
// 数据.c
#include "data.h"
struct
int age;
int number;
person;
person* person_create (void)
return malloc(sizeof(struct person));
void person_delete (person* p)
free(p);
void person_set_age (person* p, int age)
p->age = age;
int person_get_age (const person* p)
return p->age;
// 调用者:
#include "data.h"
int main()
person* p = person_create();
person_set_age(p, 50);
printf("%d", person_get_age(p));
person_delete(p);
return 0;
需要考虑的一些事项:
始终在 h 文件中使用标头保护。 切勿将空参数列表()
用于 C 函数(但始终在 C++ 中使用)。
不要在 C 中强制转换 malloc 的结果(但在 C++ 中总是这样做)。
需要在此代码中添加一些错误处理,以防 malloc 失败。
【讨论】:
不知何故,data.c 用于保存程序所需的所有数据,然后 main 文件中的 main 函数将从 data.c 文件中获取所需的数据,在您的示例中,看起来数据保存在调用者文件中?如果这个人是一个数组呢? @ratzip 使用这种方法,一切都由 data.c 文件分配,只有该文件知道如何处理它。实际数据存储在堆中......而不是文件中。如果你想要一个数组,只需person* p[n];
并在循环中调用 create 函数。【参考方案3】:
要成为一个好的设计,你必须有一个匹配的函数,在不再需要时释放分配的内存。
【讨论】:
【参考方案4】:这比其他任何事情都更重要。
只要您了解每个优点和缺点,这两个选项都有效
返回指针优点:
-
内存分配的封装
动态支持动态内存大小
返回指针缺点:
-
只有一个可能的输出参数
每次都必须在堆上分配内存
接受指针作为输入专家则完全相反:
-
多个输出参数
内存不必分配在堆上,调用者可以使用堆栈上的变量作为输出参数
接受指针作为输入缺点:
-
没有封装内存分配
不支持动态内存大小 - 大小必须是固定的或预先指定的
在您的具体示例中,我将使用输出参数,因为它是固定大小的,并且可以轻松调用而无需动态分配内存。
【讨论】:
你说的封装是什么,这是在C中的 封装不是特定于 OOP 的术语 - 您将内存分配封装在用于计算它的函数中 "只有一个可能的输出参数" Err...不是吗? C有指针。通过指针传递参数是日常的 C 编程。指针对指针的存在几乎完全是为了通过参数返回动态内存。 “每次都必须在堆上分配内存”。不,您可以编写静态内存池或使用 alloca 或类似的。而分配内存的位置与程序设计并没有真正的关系。 @IshayPeled Encapsulation是一个专门针对OOP的术语,但OO设计与语言选择无关。一些语言支持 OO 特性,但仅仅因为它们支持,并不一定意味着你的程序会因为你使用具有这些特性的语言编程而变成 OO。同样,您可以在编写 C 程序时使用 OO 设计。 ***.com/questions/351733/… Lyndon - 封装是英语中的一个术语,请查一下,看看我所说的封装内存分配是什么意思。如果您退回备忘录以上是关于从 C 函数返回指针是好还是坏设计? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章