【问题标题】:Better to pass struct, or pointer to struct?更好地传递结构或指向结构的指针?
【发布时间】:2015-06-10 13:43:47
【问题描述】:

我有一个数据结构,将在函数中读取。

我想要尽可能小的内存、代码大小和速度占用。我正在开发 AVR。

typedef struct {
    uint16_t clu;
    uint16_t num;
    uint32_t cur_rel;
} FSAVEPOS;

现在,一个将文件位置存储到结构中的函数:

// Approach 1
FSAVEPOS save_pos(const FFILE* file); // return value

// Approach 2
void save_pos(const FFILE* file, FSAVEPOS* pos); // modify reference

还有一个反转这个的函数(FFILE对象被修改):

// Approach 1
void restore_pos(FFILE* file, const FSAVEPOS pos); // pass by value

// Approach 2
void restore_pos(FFILE* file, const FSAVEPOS* pos); // pass by reference

您认为最好的主意是什么?

【问题讨论】:

  • 顺便说一句,制作价值参数const 毫无意义。这是可能的,并且使它们在函数内表现得像常量变量,但它不会使界面更清晰。我建议不要这样做。
  • 你确定是坏的吗?我特别在指针上使用 const 来表示它们不会被更改。可以说它对标量没有意义,但它不应该伤害任何东西......
  • 最小的内存占用可能意味着您必须将指针传递给该结构,但您必须尝试并查看。也许写一个小测试程序并看看编译器做了什么(优化),如果你传递一个结构,编译器可能会优化它以传递一个指针
  • @unwind: const on 指针可用于确保指针不会被无意更改。指向const struct 的指针的好处希望是显而易见的:指向的对象不能更改。这使得这在大多数时候成为通过副本传递结构的好(和更快)替代方案。对于标量,我也看不出有多大意义(但可能会强制执行单一分配策略。
  • @dwelch:ARM AAPCS 可能会在寄存器中传递小结构。对于较大的结构作为参数,这通常是一个坏主意。对于结果,如果结果仅由调用者使用,则实际上并没有什么区别(结构在堆栈上分配,其中一个指针传递给被调用者)。

标签: c struct embedded


【解决方案1】:

如果您试图最小化内存占用,那么使用指针传递比指针大得多的struct 类型会更好。明显较小的数据最好按值传递。如果指针和数据的大小差不多,那就没关系了。

假设您在 AVR32 上,您的指针将是 32 位,结构是 64 位(加上任何填充)。

这表明您最好通过指针/引用传递。但是,该结构并不是特别大,其他考虑因素可能占主导地位 - 对于您的 struct 类型来说,它并没有太多,因为它不是特别大。您需要测量相关数量(内存使用量、代码大小、速度等)才能确定。

所以我建议最好的办法是测量——这些东西会受到主机架构、编译器设置、编译器实现质量等的影响。

虽然您没有问过,但较大的类型(如 uint32_t)往往比较小的类型(如 uint16_t)有更大的对齐要求。这样做的结果是对结构的成员进行排序的相当普遍的准则,因此较大的类型首先出现在内存中。这在您的情况下也可能并不重要(可以肯定的是 16 位类型将与 16 位边界对齐,32 位类型与 32 位边界对齐,因此您的案子)。但是,如果您有三个 uint16_t 成员而不是两个,那么您最好订购一些东西,以便 uint32_t 成员排在第一位。换句话说,而不是

typedef struct
{
    uint16_t a,b,c;
    uint32_t d;
} SOME_TYPE;

您最好使用不同的订单。

typedef struct
{
    uint32_t d;
    uint16_t a,b,c;
} SOME_TYPE;

【讨论】:

  • 它是atmega328p,所以我相信指针实际上是2字节,但我可能是错的。
  • struct 是否大于平台上的指针类型,对齐问题并不是这里唯一需要考虑的事情。将 pointer 传递给函数,尤其是全局范围的函数(即非静态函数)将强制将数据写入 RAM。传入一个结构可能允许编译器将整个结构保存在寄存器中,这将减少堆栈使用并加快执行速度。在具有大量寄存器的 CPU 上尤其如此,并且可能是 C++ 中 pass-by-ref 和 pass-by-ptr 之间性能差异的原因。
  • 另外,我不认为这适用于 atmega328,但寄存器与堆栈使用的关系尤其在寄存器窗口架构上是正确的,例如SPARC,虽然我认为这在“大铁”机器之外的任何东西中都越来越少见。
  • 传递指针不会强制将数据写入 RAM,除非在非常特殊的情况下。它可以防止编译器/架构特定的优化,例如在寄存器中传递结构成员。但是这些成员更有可能从 RAM 复制到寄存器中,而不是从其他寄存器中复制 - 并且无论如何仍将存在于 RAM 中。主要的例外是仅使用特定结构类型的一个实例的琐碎程序。很少有程序这样做,而且单独的编译模型限制了编译器检测此类情况的能力。
【解决方案2】:

在第一种情况下,当您在 C 中返回一个结构时,如果它适合或通过引用隐式传递并修改了 AFAIK,它通常会在寄存器中返回。因此,在最好的情况下它比第二种方法更好,在最坏的情况下等于。

在第二种情况下,它取决于指针的大小和结构的大小等。在这种情况下,没有测量就很难说什么。不过,对于这种大小的结构,它可能不会有太大的不同。

但是,您还应该考虑与 API 的其余部分保持一致,并且如果您无法执行操作(如果操作可能失败),则有一个系统来通知错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-17
    • 2019-03-30
    • 2012-04-04
    相关资源
    最近更新 更多