仅有一个元素的结构体有什么用途?

14

例子:

struct dummy 
{
    int var;
};

为什么会使用这样的结构?我大多数情况下都在一些头文件中看到它们。

atomic_t 类型也是这样定义的。难道不能简单地定义为:

typedef int atomic_t;

9
它有良好的可扩展性,您可能需要稍后添加成员。 - Serve Laurijssen
5个回答

10

它更具可扩展性。

假设在未来,您意识到struct dummy应该包含一个名称字段,那么您可以更改其定义为:

struct dummy 
{
    int var;
    char name[30];
};

无需大量更改您的应用程序代码。


8
除了可扩展性之外,这种习惯用法还使得对那些在语义上没有意义的类型进行常规算术运算在语法上是不可能的。
例如:
typedef uint32_t myObject;
myObject x, y;
...
y = x + 3; // meaningless, but doesn’t produce an error.
           // may later cause runtime failure.

相对比较

typedef struct { uint32_t var; } myObject;
myObject x, y;
...
y = x + 3; // syntax error.

这可能看起来有些牵强,但有时非常有用。

似乎我们都做出了相同的观察。你喜欢我提供的示例吗?它与原始问题中的atomic_t名称相匹配。我自己在设计8位微处理器的闪存“文件系统”时使用了这个概念,其中需要能够从一对字节中形成虚拟页ID或物理页ID(暗示两者均为16位,并且使用任何更大的值都需要进行大量重构),但我想确保我不会意外地将物理页ID传递给期望逻辑页ID的例程,反之亦然。 - supercat

6

另一个用途是将整个数组传递给函数。

struct s {
        int a[3];
};

void f1(int a[]) // this evaluates to a pointer, same as void f1(int *a)
{
        printf("%d\n", sizeof(a));
}

void f2(struct s *obj)
{
        printf("%d\n", sizeof(obj->a));
}

int main(int argc, char **argv)
{
        int a[3] = {1, 2, 3};
        struct s obj;

        obj.a[0] = 1;
        obj.a[1] = 2;
        obj.a[2] = 3;

        f1(a);
        f2(&obj);
        return 0;
}   

// output
// 8
// 12

1
但这只是情况,因为您无法将a[3]解析为f1,因此会丢失信息。如果您将指向a的指针解析为f1,则也不需要结构体,并在取消引用后获得此结果。 - dhein

5

并非所有可以在32位中表示的东西都应该被视为数字。即使具有数值的东西也可能具有语义,这表明它们需要特殊处理。例如,假设处理器具有“原子递增”指令,但它比“普通”递增指令慢。如果想要在一个地方原子递增 fnord 并在另一个地方递减它,可以使用:

volatile int fnord;
...
atomic_inc(&fnord);
...
atomic_dec(&fnord);

然而,这样做的问题在于如果应该增加“fnord”的位置之一恰好使用“fnord ++”,而不是“atomic_inc(&fnord);”,编译器将完美地生成一个“普通”的增量指令,并且代码大多数时间可能都能正常工作,但它可能会以难以追踪的方式失败。通过用结构体替换int(并定义inline函数“atomic_inc”与其配合使用),可以防止类似“fnord ++;”的错误代码编译。它不能防止“ fnord.var ++;”,但会给程序员一个检查结构体并确定正确递增方法的机会。

1
这在大型项目中非常有用。Rust 也推荐使用这种模式,他们称之为 newtype - Franklin Yu

4

大多数情况下是为了保持兼容性,因为可能早期版本的结构体有额外的元素。

或者是为了以后添加其他元素。 (甚至结构的内部版本有不止一个成员,这对于atomic_t类型我真的可以想象。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接