C++ C/C++; 介绍
这个问题源于这个问题:。对于那些不想阅读原始问题的人来说,这是关于做这样的事情:C++ C/C++; 介绍,c++,c,macros,idioms,goto,C++,C,Macros,Idioms,Goto,这个问题源于这个问题:。对于那些不想阅读原始问题的人来说,这是关于做这样的事情: named(label1) for(int i = 0 ; i < 10 ; i++) { for(int j = 0 ; j < 10 ; j++) { if(some_condition) break(label1); // exit outer loop } } 它可能会破坏一些好看的代码。例如,由于作用域问题,以下内容未编译 int f
named(label1) for(int i = 0 ; i < 10 ; i++) {
for(int j = 0 ; j < 10 ; j++) {
if(some_condition)
break(label1); // exit outer loop
}
}
int foo() {
named(label1) for(int i = 0 ; i < 10 ; i++)
named(label2) for(int j = 0 ; j < 10 ; j++)
if(i*j<15)
cout << i*j << endl;
else
break(label2);
}
它看起来丑陋而麻烦,因为它还避免了MSVC或GCC可能生成的一些警告,如“未使用的变量”、“未引用的标签”或“建议显式大括号”。此外,如果使用不当,则不应编译,在这种情况下,错误消息是可以理解的。例如:
NAMED(loop1) for(int i = 0 ; i < 10; i++) {
NAMED(loop2) for(int j = 0 ; j < i ; j++) {
cout << i << "," << j << endl;
if(j == 5) {
BREAK(loop1); // this one is okay, we exit the outer loop
}
}
BREAK(loop2); // we're outside loop2, this is an error
}
退出特定范围:
NAMED(myscope1) {
cout<< "a";
NAMED(myscope2)
{
cout << "b";
NAMED(myscope3)
{
cout << "c";
BREAK(myscope2);
cout << "d";
}
cout << "e";
}
cout <<"f";
}
EDIT2:正如JensGustedt所注意到的,C中不允许在
if
语句中声明变量(仅限C++)。用循环代替它的另一个原因。< p>我真的不能回答C++方面,但是既然你也把它标记为C……/P>
通常使用if
/else
构造这样的东西不是一个好主意,因为你总会找到一个好的C编译器,它会为奇怪的else
匹配和类似的事情找到一个警告
我通常将用于
的构造,例如。它们避免悬挂else
(或关于它的虚假警告)。“<代码> >是是C中唯一可以放置局部变量的方法,它们不允许在<代码>中,如果或<代码>,,因为它们将在C++中。
在“安全性”方面,您可能应该声明变量register bool const
,而不仅仅是bool
。因此,没有人可以试图改变它,甚至在你背后改变它的地址
但对于您使用它的特定目的,我也不太喜欢
for
或goto
。您真正要做的是展开堆栈。在C语言中,有一种结构可以正确地实现这一点,即setjmp
/longjmp
。在C++中,可能是<代码>尝试< /Calp>/<代码> catch < /C> > < /P> < p>我假定你已经测试过了,但是…您在NAMED(bn)
中声明的bool
变量以后在同一块中声明的if语句存在时是否仍然可用?(:除非是这样,否则你的习语是行不通的。)
我认为这是安全的:
{
NAMED(one) { ... }
}
BREAK(one);
编译器会在中断(nb)上大惊小怪代码>行
但这看起来仍然不安全:
{
NAMED(bn) { ... }
BREAK(bn);
}
不安全的意思是,编译器仍然可以接受定义的变量,而不是大惊小怪。但它可能会形成一个无限循环,从而悄悄地破坏您的程序
-杰西
PS:它不会干扰try..finally
,因为finally
块定义为执行,而不管您如何退出try
块。因此,只要您不尝试避免最终
块,您就可以了
p(PS)S:我所看到的与其他构造的唯一真正奇怪的交互是:
#if DEBUG
NAMED(bn)
#endif
while(true)
{
BREAK(BN);
}
这是病态的-这将在调试时编译良好,但在发布时会出现编译器错误。这非常聪明,但老实说,我认为您最好使用goto
。每个人都知道goto
。读过这篇文章的人可能会坐下来,试图弄清楚宏在做什么,然后看看它们使用的代码。在第一次阅读源代码时,这不会占用很多时间。@Zack:这种构造的目的是避免直接使用goto
,并为退出嵌套作用域提供更安全的语法。在这里,标签是自动创建的,以跳出范围,减少出错的风险。@chris:我完全同意你的观点,这就是为什么我认为它不是为了在实际代码中使用。我喜欢这种语法,我想在我的个人项目中使用它。其他人不应该读我的代码。我只想确保它没有隐藏的设计缺陷,它可以在一些异常情况下悄悄地中断我的代码(可能与异常处理、析构函数调用等有关)。“你能看到我的代码中的错误”的问题是否更适合于?我标记了C&C++,因为它不依赖于任何C++特定的特性。关于for
vsif
的选择,我认为这是一个非常好的建议,因为如果(条件)打破(标签),我会得到一些虚假的警告;否则{}。定义变量register bool const
的第二个建议也很有趣,可以防止恶意用户劫持它。谢谢。我绝对不会推荐范围退出的异常语义。对控制流使用try/catch既可怕又糟糕,因为这不是它的目的。他的宏定义时没有悬挂else
s,因此编译器应该都很高兴。两个宏都发出完整的语句。正如我所说,if
子句中的变量声明在C中是不可能的。就我个人而言,我宁愿使用宏而不是setjmp()
,longjmp()
,BREAK
要求在其作用域中定义布尔变量。NAMED
宏创建了一个新的作用域来存储它,因此在这种情况下,它不会编译,编译器会抛出“\u您不能使用\u NAMED\u BREAK\u如果\u您在\u bn\u范围之外\u bn\u”没有在该作用域中声明,当然如果仅在调试中定义了NAMED,它将不起作用。也就是说,比起运行时崩溃/未定义的行为,我更喜欢编译错误:)
if(true)
BREAK(label);
{
NAMED(one) { ... }
}
BREAK(one);
{
NAMED(bn) { ... }
BREAK(bn);
}
#if DEBUG
NAMED(bn)
#endif
while(true)
{
BREAK(BN);
}