【问题标题】:Why declare a struct that only contains an array in C?为什么要在 C 中声明一个只包含数组的结构?
【发布时间】:2011-10-21 10:45:15
【问题描述】:

我遇到了一些包含以下内容的代码:

struct ABC {
    unsigned long array[MAX];
} abc;

什么时候使用这样的声明才有意义?

【问题讨论】:

    标签: c arrays coding-style struct


    【解决方案1】:

    它允许您将数组按值传递给函数,或从函数中按值返回。

    结构可以按值传递,不像数组在这些上下文中衰减为指针。

    【讨论】:

    • 谨防对不内联的函数使用超过 16 或 32 字节的数组执行此操作:通过 const-reference 传递它们更有效,除非被调用者已经需要 tmp 副本它可以摧毁。如果调用/返回没有优化掉,那么一个中到大的数组(数千字节)是通过值传递的可怕的事情。
    【解决方案2】:

    另一个优点是它抽象了大小,因此您不必在任何声明此类对象的代码中都使用[MAX]。这也可以通过

    来实现
    typedef char ABC[MAX];
    

    但是你有一个更大的问题:你必须知道ABC是一个数组类型(即使你在声明ABC类型的变量时看不到这一点)否则你会得到ABC 在函数参数列表中的含义与在变量声明/定义中的含义不同。

    另一个优点是该结构允许您以后根据需要添加更多元素,而无需重写大量代码。

    【讨论】:

      【解决方案3】:

      您可以复制结构并从函数返回结构。

      你不能用数组来做到这一点 - 除非它是结构的一部分!

      【讨论】:

        【解决方案4】:

        你可以这样复制。

        struct ABC a, b;
        ........
        a = b;
        

        对于数组,您需要使用 memcpy 函数或循环来分配每个元素。

        【讨论】:

        • (因此它允许更简洁的代码 - 它不会对速度等产生任何影响)
        • 这很有用。不幸的是你不能这样做 if (a == b)!?!这是多么不一致。对于 C++,它查找 == 运算符。在 C 中它说“二进制操作数无效 ==”。
        【解决方案5】:

        您可以使用 struct 来制作新类型的数据,例如 string。你可以定义:

        struct String {
            char Char[MAX];
        };
        

        或者您可以创建一个数据列表,您可以通过函数参数使用它或在您的方法中返回它。 struct 比数组更灵活,因为它可以支持一些运算符,如 = 并且您可以在其中定义一些方法。

        希望对你有用:)

        【讨论】:

        • 基本上,它是 C 最接近于创建类的东西。我喜欢这个答案,因为它最接近指出这一点。
        • C 中没有方法之类的东西。C 中的结构是普通的旧数据。它默认支持 = 运算符(其他答案显示这是这样做的原因),但这具有误导性,主要适用于 C++,而不是 C。
        • @J Sternberg:“方法”只是将子例程视为与它们影响的数据“对象”相关的一种方式。您当然可以在 C 中创建对它们进行操作的“对象”和“方法”的“类”。该语言只是没有正式定义这些东西。如果您想在 C 中创建更好的抽象,将内容填充到结构中通常是最好的方法。
        • 此外,如果您真的想在 C 中“创建”方法,您可以使用函数指针(是的,是的,复杂的语法,无数据保护等)将函数与它们所创建的数据相关联操作。您必须在第一个参数中传入“self”(如果需要,您甚至可以将其命名为“this”),因为在 C 中的函数内部不会自动创建 this 指针。当然,这都是体操,你得到在 C++ 中默认情况下是这样的,尽管确实可能会有隐藏的开销作为奖励......
        【解决方案6】:

        使用这样的struct 的另一个优点是它在使用这样的struct 的地方强制类型安全;特别是如果您有两种类型,由用于不同目的的相同大小的数组组成,这些类型将帮助您避免意外地不恰当地使用数组。

        如果您不将数组包装在struct 中,您仍然可以为其声明typedef:这具有struct 的一些优点 – • 类型声明一次, • 大小为自动更正, • 代码的意图变得更清晰, • 代码更易于维护 - 但您失去了 ◦ 严格的类型安全性, ◦ 复制和返回类型值的能力,以及 ◦ 以后添加成员而不破坏其余部分的能力你的代码。给定类型的裸数组的两个typedefs 仅在它们具有不同大小时才会产生不同的类型。此外,如果在函数参数中使用 typedef 而不使用 *,则它等效于 char *,从而大大降低了类型安全性。

        总结

        typedef struct A_s_s { char m[113]; } A_s_t; // Full type safey, assignable
        typedef char   A_c_t[113];                   // Partial type-safety, not assignable
        
        A_s_t          v_s(void);     // Allowed
        A_c_t          v_c(void);     // Forbidden
        
        void           s__v(A_s_t);     // Type-safe, pass by value
        void           sP_v(A_s_t *);   // Type-safe
        void           c__v(A_c_t);     // UNSAFE, just means char * (GRRR!)
        void           cP_v(A_c_t *);   // SEMI-safe, accepts any array of 113
        

        【讨论】:

          【解决方案7】:

          一个结构可以包含数组初始化、复制和fini 函数,这些函数模拟了OOP 内存管理范例的一些优点。事实上,很容易扩展这个概念来编写一个通用的内存管理实用程序(通过使用 sizeof() 结构来准确知道正在管理多少字节)来管理任何用户定义的结构。许多用 C 编写的智能生产代码库大量使用这些,并且通常从不使用数组,除非它的范围非常本地化。

          事实上,对于嵌入在结构中的数组,您可以在任何时候想要访问该数组时执行其他“智能操作”,例如边界检查。同样,除非数组范围非常有限,否则使用它并在程序之间传递信息是一个坏主意。迟早,您会遇到让您彻夜难眠并破坏您的周末的错误。

          【讨论】:

          • 这并没有回答为什么人们可能会使用包含一个数组的struct
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-05
          • 1970-01-01
          • 2018-05-13
          相关资源
          最近更新 更多