灵活的数组成员和指针成员:优缺点?

2024-05-07

使用灵活数组成员 (FAM) 或指针成员有什么区别?在这两种情况下,必须逐个元素地进行 malloc 和影响。但对于 FAM,内存分配是针对整个结构完成的,而对于 ptr 成员,内存分配是仅针对 ptr 成员完成的(参见代码)。这两种方法的优缺点是什么?

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

typedef struct farr_mb {
    int lg;
    int arr[];
} Farr_mb;

typedef struct ptr_mb {
    int lg;
    int * ptr;
} Ptr_mb;

 int main() {

    int lg=5;

    Farr_mb *a=malloc(sizeof(Farr_mb)+lg*sizeof(int));
    Ptr_mb b; b.ptr=malloc(lg*sizeof(int));

    for (int i=0;i<lg;i++) a->arr[i]=i;
    for (int i=0;i<lg;i++) b.ptr[i]=i;

    for (int i=0;i<lg;i++) printf("%d \t",(a->arr)[i]=i);
    printf("\n");
    for (int i=0;i<lg;i++) printf("%d \t",(b.ptr)[i]=i);

    return 0;
}

在讨论优点和缺点之前,让我们先看一些现实世界的例子。

假设我们希望实现一个哈希表,其中每个条目都是动态管理的数组elements:

struct hash_entry {
    size_t              allocated;
    size_t              used;
    element             array[];
};

struct hash_table {
    size_t              size;
    struct hash_entry **entry;
};
#define HASH_TABLE_INITIALIZER { 0, NULL }

这实际上使用both。哈希表本身是一个具有两个成员的结构。这sizemember表示哈希表的大小,entrymember 是一个指向哈希表条目指针数组的指针。这样,每个未使用的条目只是一个NULL指针。当向哈希表条目添加元素时,整个struct entry可以重新分配(对于sizeof (struct entry) + allocates * sizeof (element)或者释放,只要对应的指针在entry成员在struct hash_table已相应更新。

如果我们使用element *array相反,我们需要使用struct hash_entry *entry: in the struct hash_table;或分配struct hash_entry与数组分开;或分配两者struct hash_entry和单个块中的数组,其中array指针指向相同的之后struct hash_entry.

这样做的费用将是额外的两倍size_t用于每个未使用的哈希表槽的内存价值,以及访问时额外的指针取消引用elements。 (或者,要获取数组的地址,请进行两次连续的指针取消引用,而不是一次指针取消引用加上偏移量。)如果这是在实现中大量使用的关键结构,则该成本在分析中会很明显,并对缓存性能产生负面影响。对于随机访问,元素越大array是个less然而,差异是存在的;当arrays 很小,并且适合与相同的缓存行(或几个缓存行)allocated and used成员。

我们通常不想让entry成员在struct hash_table灵活的数组成员,因为这意味着您不再可以使用静态声明哈希表struct hash_table my_table = HASH_TABLE_INITIALIZER;;您需要使用指向表的指针和初始化函数:struct hash_table *my_table; my_table = hash_table_init();或类似的。

我有另一个例子 https://stackoverflow.com/a/34862940/1475978使用指针成员和灵活数组成员的相关数据结构。它允许使用类型变量matrix表示任何二维矩阵double条目,即使一个矩阵是另一个矩阵的视图(例如,转置、块、行或列向量,甚至对角向量);这些视图都是相等的(与 GNU 科学图书馆不同,其中矩阵视图由单独的数据类型表示)。这种矩阵表示方法使编写强大的数值线性代数代码变得容易,并且随后的代码比使用 GSL 或 BLAS+LAPACK 时更具可读性。在我看来,就是这样。


因此,让我们从如何选择使用哪种方法的角度来看看利弊。 (出于这个原因,我不会将任何功能指定为“赞成”或“反对”,因为决定取决于上下文、每个特定的用例。)

  • 具有灵活数组成员的结构不能静态初始化。您只能通过指针引用它们。

    您可以使用指针成员声明和初始化结构。如上面的示例所示,使用预处理器初始化宏可能意味着您不需要初始化函数。例如,一个函数接受一个struct hash_table *table参数总是可以使用调整指针数组的大小realloc(table->entry, newsize * sizeof table->entry[0]),即使当table->entry一片空白。这减少了所需功能的数量,并简化了它们的实现。
     

  • 通过指针成员访问数组可能需要额外的指针取消引用。

    如果我们将对具有数组指针的静态初始化结构中的数组的访问与通过静态指针引用的具有灵活数组成员的结构进行比较,则会进行相同数量的取消引用。

    如果我们有一个获取结构体地址作为参数的函数,那么通过指针成员访问数组元素需要两次指针取消引用,而访问灵活数组元素只需要一次指针取消引用和一次偏移量。如果数组元素足够小并且数组索引足够小,使得访问的数组元素位于同一个高速缓存行中,则灵活的数组成员访问通常会明显更快。对于较大的阵列,性能差异往往微不足道。然而,这在硬件架构之间确实有所不同。
     

  • 通过指针成员重新分配数组可以隐藏使用该结构作为不透明变量的复杂性。

    这意味着,如果我们有一个函数接收指向结构的指针作为参数,并且该结构有一个指向动态分配的数组的指针,则该函数可以重新分配该数组,而调用者不会看到结构地址本身有任何变化(仅结构contents改变)。

    但是,如果我们有一个函数接收指向具有灵活数组成员的结构的指针,则重新分配数组意味着重新分配整个结构。这可能会修改结构的地址。由于指针是按值传递的,因此调用者看不到修改。因此,可以调整灵活数组成员大小的函数必须接收一个指向具有灵活数组成员的结构的指针。

    如果函数仅检查具有灵活数组成员的结构的内容,例如计算满足某些条件的元素数量,则指向该结构的指针就足够了;并且指针和指向的数据都可以被标记const。这可能有助于编译器生成更好的代码。此外,所有访问的数据在内存中都是线性的,这有助于更复杂的处理器更有效地管理缓存。 (要对具有指针成员的数组执行相同操作,需要将指针传递给数组,至少还需要将大小字段作为计数函数的参数传递,而不是传递指向包含这些值的结构的指针.)
     

  • 具有灵活数组成员的未使用/空结构可以由 NULL 指针(指向此类结构)表示。当您有数组的数组时,这可能很重要。

    对于具有灵活数组成员的结构,外部数组只是一个指针数组。对于具有指针成员的结构,外部数组可以是结构数组,也可以是指向结构的指针数组。

    如果结构具有公共类型标记作为第一个成员,并且您使用这些结构的并集,则两者都可以支持不同类型的子数组。 (不幸的是,在这种情况下“使用”的含义是有争议的。有人声称您需要通过联合访问数组,我声称这样的联合的可见性就足够了,因为其他任何东西都会破坏大量现有的 POSIX C 代码;基本上所有服务器端 C 代码都使用套接字。)

目前我能想到的主要就是这些。这两种形式在我自己的代码中无处不在,而且我对这两种形式都没有任何问题。 (特别是,我更喜欢使用无结构的辅助函数,该函数会破坏结构,以帮助检测早期测试中的释放后使用错误;并且我的程序通常不会出现任何与内存相关的问题。)

如果我发现我遗漏了重要的方面,我将编辑上面的列表。因此,如果您有建议或认为我忽略了上述内容,请在评论中告诉我,以便我可以进行适当的验证和编辑。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

灵活的数组成员和指针成员:优缺点? 的相关文章

  • C# 打印问题(RichTextBox)

    我想打印我的 RichTextBox eintragRichTextBox 的内容 我现在有这个代码 private void druckenPictureBox Click object sender EventArgs e PrintD
  • 添加 Nullable int 时保持 null?

    我想添加可为空的int 并保留null当所有值都是null 我想要这个结果 1 2 3 1 null 1 null null null O null 0 问题是 如果我将一个值与 null 相加 结果为 null int i1 1 int
  • Poco c++Net:Http 从响应中获取标头

    我使用 POCO C Net 库进行 http 我想尝试制定持久缓存策略 首先 我认为我需要从缓存标头中获取过期时间 并与缓存值进行交叉检查 如果我错了 请告诉我 那么我如何从中提取缓存头httpResponse 我已经看到你可以用 Jav
  • 为什么 F# 的默认集合是排序的,而 C# 的不是?

    当从 C 世界迁移到 F 最惯用的可能 思维方式时 我发现了这个有趣的差异 在 C 的 OOP mutable 世界中 默认的集合集合似乎是HashSet https learn microsoft com en us dotnet api
  • 检测wlan是否关闭

    任何人都可以给我一个提示 如何在 Windows Phone 上以编程方式检测 C 8 1 应用程序 不是 8 0 是否启用 禁用 WLAN 我不想更改这些设置 只是需要知道 该解决方案是一个 Windows 8 1 通用应用程序 Wind
  • std::call_once 可重入且线程安全吗?

    std call once http en cppreference com w cpp thread call once是线程安全的 但它也是可重入的吗 我使用 VS2012 调试和发布 进行的测试表明 调用std call once从单
  • 从模板切换传递的类型

    在 C 中是否可以检查传递给模板函数的类型 例如 template
  • 如何制作可启动程序?

    所以 这个问题可能看起来很奇怪 但假设我编译了 int main void int x 3 int y 4 int z x y 是否可以让CPU这样运行 如何 例如 这允许我写入监视器吗 如果我没记错的话 内存中有些地方可以写入要显示的内容
  • 一元 +/- 运算符如何可能导致“-a”或“+a”中的整数提升,“a”是算术数据类型常量/变量?

    这句看似微不足道的台词摘自我的迈克 巴纳汉和布雷迪的 C 书 第 2 8 8 2 节 http publications gbdirect co uk c book chapter2 expressions and arithmetic h
  • 获取 boost Spirit 语法中的当前行

    我正在尝试使用 boostspirit 获取正在解析的文件的当前行 我创建了一个语法类和结构来解析我的命令 我还想跟踪在哪一行找到命令并将其解析到我的结构中 我将 istream 文件迭代器包装在 multi pass 迭代器中 然后将其包
  • 访问 ascx 文件中的母版页控件

    我有一个母版页文件 其中包含 2 个面板控件中的 2 个菜单 我还使用控件来检查用户是否登录并获取用户类型 根据我想要显示 隐藏面板的类型 控件本身不在母版页中引用 而是通过 CMS 系统动态引用 我想在用户控件中使用findcontrol
  • 使用查询表达式对 List 进行排序

    我在使用 Linq 订购这样的结构时遇到问题 public class Person public int ID get set public List
  • UI 函数在快速事件完成之前触发

    我有一个停靠在 Silverlight 应用程序中的 Web 浏览器框架 有时会在其上弹出全窗口 XAML Silverlight UI 元素 我已经或多或少修复了一个老问题 即 Web 框架的内容似乎与 Silverlight 内容不能很
  • 从浏览器访问本地文件?

    您好 我想从浏览器访问系统的本地文件 由于涉及大量安全检查 是否可以通过某种方式实现这一目标 或使用 ActiveX 或 Java Applet 的任何其他工作环境 请帮帮我 要通过浏览器访问本地文件 您可以使用签名的 Java Apple
  • 在 OpenGL 中渲染纹理 1 到 1

    所以我想做的是使用 OpenGL 和 C 将纹理渲染到平面上 作为显示图像的一种方式 但是我需要确保在渲染纹理时没有对纹理进行任何处理 抗锯齿 插值 平滑 模糊等 这是 OpenGL 处理渲染纹理的默认方式吗 或者是否需要设置一些标志才能禁
  • ASP.NET MVC 路由:如何从 URL 中省略“索引”

    我有一个名为 StuffController 的控制器 具有无参数索引操作 我希望从表单中的 URL 调用此操作mysite com stuff 我的控制器定义为 public class StuffController BaseContr
  • .NET 4 的条件编译[重复]

    这个问题在这里已经有答案了 可能的重复 条件编译和框架目标 https stackoverflow com questions 2923210 c sharp conditional compilation and framework ta
  • 通过 Tab 键浏览 XML 文档字段

    In VB NET you can move through the fields in the XML member documentation with the Tab key 这在 C 中不起作用 还有其他方法吗 除了用鼠标将光标放在
  • DataContractSerializer 事件/委托字段问题

    在我的 WPF 应用程序中 我正在使用DataContractSerializer序列化对象 我发现它无法序列化具有事件或委托声明的类型 考虑以下失败的代码 Serializable public abstract class BaseCl
  • 如何将 SQL“LIKE”与 LINQ to Entities 结合使用?

    我有一个文本框 允许用户指定搜索字符串 包括通配符 例如 Joh Johnson mit ack on 在使用 LINQ to Entities 之前 我有一个存储过程 该存储过程将该字符串作为参数并执行以下操作 SELECT FROM T

随机推荐