【GNU笔记】【C扩展系列】表达式中的语句和声明

【GNU笔记】【C扩展系列】表达式中的语句和声明 Statements and Declarations in Expressions

表达式中的语句和声明 Statements and Declarations in Expressions

在GNU C中,用括号括起来的复合语句可以作为一个表达式出现。这允许你在表达式中使用循环(loops)、switches和局部变量(local variables)。

回顾一下,复合语句是一个由大括号包围的语句序列;在这个结构(construct)中,小括号括在大括号外面。例如:

({ int y = foo (); int z;
   if (y > 0) z = y;
   else z = - y;
   z; })
   

foo ()绝对值的有效表达式(尽管比必要的表达式稍微复杂一些)。

复合语句中的最后一件事应该是一个表达式,后面跟一个分号;这个子表达式的值是整个结构的值。(如果你在大括号内最后使用其他类型的语句,则该结构的类型为void,因此实际上没有任何值)。

这个特性在使宏定义 “安全”(使它们对每个操作数只计算一次)方面特别有用。例如,"maximum"函数在标准C语言中通常被定义为一个宏,如下所示。

#define max(a,b) ((a) > (b) ? (a) : (b))

但这个定义会对a或b进行两次计算,如果操作数有副作用,结果就会很糟糕。在GNU C中,如果你知道操作数的类型(这里视为int),你可以通过如下定义宏来避免这个问题。

#define maxint(a,b) \
  ({int _a = (a), _b = (b); _a > _b ? _a : _b; })
  

请注意,引入变量声明(就像我们在maxint中所做的那样)可能会导致变量隐藏(Variable Shadowing),所以这个使用max宏的示例会产生正确的结果:

int _a = 1, _b = 2, c;
c = max (_a, _b);

这个使用maxint的示例却不会:

int _a = 1, _b = 2, c;
c = maxint (_a, _b);

例如,当我们递归地使用此模式时,可能会出现这个问题,就像这样:

#define maxint3(a, b, c) \
  ({int _a = (a), _b = (b), _c = (c); maxint (maxint (_a, _b), _c); })
  

常量表达式中不允许嵌入语句,例如枚举常量(enumeration constant)的值、位域(bit-field)的宽度或静态变量(static variable)的初始值。

如果你不知道操作数的类型,你仍然可以这样做,但你必须使用typeofor __auto_type(参见Typeof)。

在G++中,语句表达式的结果值经历数组和函数指针衰减,并通过值返回给封闭表达式(enclosing expression)。例如,如果A是一个类,那么

        A a;

        ({a;}).Foo ()
        

构造了一个临时的A对象来保存语句表达式的结果,并用于调用Foo。因此,Foo观察到的这个this指针不是a的地址。

在语句表达式中,任何在语句中创建的临时变量都会在该语句结束时销毁。这使得宏内的语句表达式与函数调用略有不同。在后一种情况下,在参数计算过程中引入的临时变量,在包含函数调用的语句结束时被销毁。在语句表达式中,它们在语句表达式期间被销毁。例如,

#define macro(a)  ({__typeof__(a) b = (a); b + 3; })
template<typename T> T function(T a) { T b = a; return b + 3; }

void foo ()
{
  macro (X ());
  function (X ());
}

有不同的地方,临时变量被销毁。在这种 macro情况下,临时变量Xb的初始化之后被销毁。在function情况下,临时变量在函数返回时被销毁。

这些考虑意味着在设计用于C++的头文件中使用这种形式的语句表达式可能是个坏主意。(请注意,GNU C Library的某些版本包含了使用语句表达式的头文件,这恰恰导致了这个bug。)

不允许使用goto跳转到语句表达式,或在语句表达式外部使用switch语句,在语句表达式内部使用带有casedefault标签。使用计算goto(参见Labels as Values)跳转到语句表达式会产生未定义的行为。允许跳出语句表达式,但如果语句表达式是更大的表达式的一部分,则未指定该表达式的哪些其他子表达式已被计算,除非语言定义要求在语句表达式之前或之后计算某些子表达式。语句表达式中的breakcontinue语句用于whiledofor循环或switch语句的条件或for语句的init或increment表达式,如果有的话就跳到外部循环或switch语句(否则就是错误),而不是跳到它出现的条件或init或increment表达式的循环或switch语句中。在任何情况下,与函数调用一样,语句表达式的评估不会与包含表达式的其他部分的评估交错进行。比如说。在任何情况下,与函数调用一样,语句表达式的计算不会与包含表达式的其他部分的计算交叉。例如,

  foo (), (({ bar1 (); goto a; 0; }) + bar2 ()), baz();
  

调用fooandbar1,不调用baz,但可能会也可能不会调用bar2。如果bar2被调用,则在foo之后和bar1之前调用它。

[参考资料]

6.1 Statements and Declarations in Expressions

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值