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
    vs
    if
    的选择,我认为这是一个非常好的建议,因为如果(条件)打破(标签),我会得到一些虚假的警告;否则{}。定义变量
    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);
        }