简体   繁体   中英

What is the purpose of anonymous { } blocks in C style languages?

What is the purpose of anonymous { } blocks in C style languages (C, C++, C#)

Example -



void function()
{

  {
    int i = 0;
    i = i + 1;
  }

  {
    int k = 0;
    k = k + 1;
  }

}

Edit - Thanks for all of the excellent answers!

它将变量的范围限制在 { } 内的块内。

Brackets designate an area of scope - anything declared within the brackets is invisible outside of them.

Furthermore, in C++ an object allocated on the stack (eg without the use of 'new') will be destructed when it goes out of scope.

In some cases it can also be a way to highlight a particular piece of a function that the author feels is worthy of attention for people looking at the source. Whether this is a good use or not is debatable, but I have seen it done.

They are often useful for RAII purposes, which means that a given resource will be released when the object goes out of scope. For example:

void function()
{
    {
        std::ofstream out( "file.txt" );
        out << "some data\n";
    }
    // You can be sure that "out" is closed here
}

{ ... } opens up a new scope

In C++, you can use them like this:

void function() {
    // ...
    {
        // lock some mutex.
        mutex_locker lock(m_mutex);
        // ...
    }
    // ...
}

Once control goes out of the block, the mutex locker is destroyed. And in its destructor, it would automatically unlock the mutex that it's connected to. That's very often done, and is called RAII (resource acquisition is initialization) and also SBRM (scope bound resource management). Another common application is to allocate memory, and then in the destructor free that memory again.

Another purpose is to do several similar things:

void function() {
    // set up timer A
    {
        int config = get_config(TIMER_A);
        // ... 
    } 

    // set up timer B
    {
        int config = get_config(TIMER_B);
        // ...
    } 
}

It will keep things separate so one can easily find out the different building blocks. You may use variables having the same name, like the code does above, because they are not visible outside their scope, thus they do not conflict with each other.

Another common use is with OpenGL's glPushMatrix() and glPopMatrix() functions to create logical blocks relating to the matrix stack:

glPushMatrix();
{
    glTranslate(...);
    glPushMatrix();
    {
        glRotate(...);
        // draw some stuff
    }
    glPopMatrix();
    // maybe draw some more stuff
}
glPopMatrix();
class ExpensiveObject {
public:
    ExpensiveObject() {
        // acquire a resource
    }
    ~ExpensiveObject() {
        // release the resource
    }
}

int main() {
    // some initial processing
    {
        ExpensiveObject obj;
        // do some expensive stuff with the obj
    } // don't worry, the variable's scope ended, so the destructor was called, and the resources were released
    // some final processing
}

By creating a new scope they can be used to define local variables in a switch statement.

eg

switch (i)
{
    case 0 :
        int j = 0;   // error!
        break;

vs.

switch (i)
{
    case 0 :
    {
        int j = 0;   // ok!
    }
    break;

They're very often used for scoping variables, so that variables are local to an arbitrary block defined by the braces. In your example, the variables i and k aren't accessible outside of their enclosing braces so they can't be modified in any sneaky ways, and that those variable names can be re-used elsewhere in your code. Another benefit to using braces to create local scope like this is that in languages with garbage collection, the garbage collector knows that it's safe to clean up out-of-scope variables. That's not available in C/C++, but I believe that it should be in C#.

One simple way to think about it is that the braces define an atomic piece of code, kind of like a namespace, function or method, but without having to actually create a namespace, function or method.

You are doing two things.

  1. You are forcing a scope restriction on the variables in that block.
  2. You are enabling sibling code blocks to use the same variable names.

Scoping of course. (Has that horse been beaten to death yet?)

But if you look at the language definition, you see patterns like:

  • if ( expression ) statement
  • if ( expression ) statement else statement
  • switch ( expression ) statement
  • while ( expression ) statement
  • do statement while ( expression ) ;

It simplifies the language syntax that compound-statement is just one of several possible statement's .


compound-statement : { statement-list opt }

statement-list :

  • statement
  • statement-list statement

statement :

  • labeled-statement
  • expression-statement
  • compound-statement
  • selection-statement
  • iteration-statement
  • jump-statement
  • declaration-statement
  • try-block

As far as I understand, they are simply for scoping. They allow you to reuse variable names in the parent/sibling scopes, which can be useful from time to time.

EDIT: This question has in fact been answered on another Stack Overflow question . Hope that helps.

As the previous posters mentioned, it limits the use of a variable to the scope in which it is declared.

In garbage collected languages such as C# and Java, it also allows the garbage collector to reclaim memory used by any variables used within the scope (although setting the variables to null would have the same effect).

{
    int[] myArray = new int[1000];
    ... // Do some work
}
// The garbage collector can now reclaim the memory used by myArray

A useful use-cas ihmo is defining critical sections in C++ . eg:

int MyClass::foo()
{    
   // stuff uncritical for multithreading
   ...
   {
      someKindOfScopeLock lock(&mutexForThisCriticalResource);
      // stuff critical for multithreading!
   }
   // stuff uncritical for multithreading
   ...    
}

using anonymous scope there is no need calling lock/unlock of a mutex or a semaphore explicitly.

It's about the scope, it refers to the visibility of variables and methods in one part of a program to another part of that program, consider this example:

int a=25;
int b=30;
{ //at this point, a=25, b=30
     a*=2; //a=50, b=30
     b /= 2; //a=50,b=15
     int a = b*b; //a=225,b=15  <--- this new a it's
                  //                 declared on the inner scope
}
//a = 50, b = 15

If you are limited to ANSI C, then they could be used to declare variables closer to where you use them:

int main() {
    /* Blah blah blah. */
    {
        int i;
        for (i = 0; i < 10; ++i) {
        }
    }
}

Not neccessary with a modern C compiler though.

我将它用于需要临时变量的代码块。

One thing to mention is that scope is a compiler controlled phenomenon. Even though the variables go out of scope (and the compiler will call any destructors; POD types are optimised immediately into the code), they are left on the stack and any new variables defined in the parent scope do not overwrite them on gcc or clang (even when compiling with -Ofast). Except it is undefined behaviour to access them via address because the variables have conceptually gone out of scope at the compiler level -- the compiler will stop you accessing them by their identifiers.

#include <stdio.h>
int main(void) {
  int* c;
  {
    int b = 5; 
    c=&b;
  }
  printf("%d", *c); //undefined behaviour but prints 5 for reasons stated above
  printf("%d", b); //compiler error, out of scope
  return 0;
}

Also, for, if and else all precede anonymous blocks aka. compound statements, which either execute one block or the other block based on a condition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM