例子:
struct dummy
{
int var;
};
为什么使用这样的结构?大多数情况下,我在一些头文件中看到过它们。
atomic_t
类型也是这样定义的。不能简单地使用:
typedef int atomic_t;
它更具可扩展性。
假设将来,你意识到struct dummy
应该包含一个名称字段,那么你可以将它的定义更改为:
struct dummy
{
int var;
char name[30];
};
无需更改大部分应用程序代码。
除了可扩展性之外,这个习语还使得在语法上不可能对意义如此语义上没有意义的类型进行正常的算术运算。
例如:
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.
这可能看起来很做作,但它有时非常有用。
另一种用途是将整个数组传递给函数。
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
并非所有可以用 32 位表示的事物都应视为数字。即使是具有数值的事物也可能具有暗示它们需要特殊处理的语义。例如,假设一个处理器有一个“原子增量”指令,但它比“正常”增量指令慢。如果想在一个地方原子地增加fnord
并在另一个地方减少它,可以使用:
volatile int fnord;
...
atomic_inc(&fnord);
...
atomic_dec(&fnord);
然而,一个问题是,如果应该递增的地方之一碰巧fnord
使用了fnord++
而不是atomic_inc(&fnord);
可能会以难以追踪的方式失败。
用结构替换int
(并定义atomic_inc
内联函数以使用它)将防止错误代码如fnord++;
编译。它不会防范,fnord.var++;
但会给程序员一个检查结构的机会,看看增加它的正确方法是什么。
主要是为了保持兼容性,因为之前的结构可能有额外的元素。
或者因为它可能打算稍后添加其他元素。(甚至结构的内部版本也不止一个成员(我真的可以想象atomic_t
-type。)