Fork me on GitHub
Showing posts with label variable. Show all posts
Showing posts with label variable. Show all posts

4/07/2012

[C] variable/function scope in C

習慣 C++ 後換到 C 這種更精簡的語言,反而不是很清楚變數的生命週期,以及在不同程式單元共用的方式,所以 Google 了一篇 Scope and Storage Classes in C,在此筆記一下!

總而言之,4 scope, 4 specifier, 2 modifier

4 scope

Block
所謂 block 就是 { 和 } 包圍起來的區域,block scope 就是指自變數宣告處一直到 } 的生命週期。

Function
在 { 和 } 內都存活的生命週期。In C, only the goto label has function scope。

File
即 static + (program scope variable)

Program
當變數在函數以外宣告則為 program scope,也就是所謂的全域變數。全域變數欲在其他檔案存取時,需以 extern 關鍵字 declare 一次。

4 specifier

auto
只有在 block scope 的變數可以使用這個 specifier,但是 block scope 的變數本來就是 temp 的,所以這個很少用。

static
static 可以用在 block scope 或 program scope,當用在 block scope 時,指稱該變數具有 permanent duration,也就是出了該 block 該變數仍存在的意思;當用在 program scope 時,也就是用來修飾全域變數時,即把 program scope 縮小為 file scope,除了該檔案其他檔案無法使用。

register
建議 compiler 將變數儲存到 CPU 暫存器的修飾子。

extern
欲在另一檔案中使用某檔案之 program scope 變數,需要在該檔案 file scope 內搭配 extern 再宣告一次欲使用之變數。

2 modifier

const
const 變數宣告即必須初始化,並且之後不能再被賦值。

volatile
When the C compiler optimizes your program automatically, it intends to not update the value held by the variable unless the variable is on the left side of an assignment operator (=).

volatile 變數 tells the compiler not to optimize any expressions of the variable because the value saved by the variable may be changed without execution of any explicit assignment statement.

以上是變數的生命週期,另外函數的 scope 則相對簡單,沒有修飾子的函數即為全域函數,如果在前面加上 static 修飾子,就變成 file scope。如果另外一個檔案要使用 file scope 的函式,則需加上 extern 修飾子聲明。

接下來一定會有一個疑問,就是在 C 的世界裡面,要怎麼實現 private function 呢?在 C 裡頭並沒有強的 module 概念,可以參考 Ref2 當中的 translation unit,一個 translation unit 就是 source file 加上他包含的 header file (包括巢狀 include 當中的)。

所以,如果要實現 private function 的話,一樣也是用 static 關鍵字,將 function scope 縮小至 file scope,然後需要調用該函數的內部函數記得將 declaration 加到 header file 當中即可。

References

  1. How to implement a “private/restricted” function in C?
  2. Concept of modules in c

-– EOF --

6/26/2011

[C++] local variable 為什麼可以離開 scope 而被存取

在 stackoverflow 看到一則有趣的討論串,問題是這樣的:

int * foo()
{
    int a = 5;
    return &a;
}

int main()
{
    int* p = foo();
    cout << *p;
    *p = 8;
    cout << *p;
}

And the code is just running with no runtime exceptions! The output was 5 8! How can it be? Isn't the memory of a local variable inaccessible outside its function?

意思就是為什麼 a 這個函數的局部變數還存活到離開了函數的 scope 呢?下面一個答案的比喻真是妙極了:
You rent a hotel room. You put a book in the top drawer of the bedside table and go to sleep. You check out the next morning, but "forget" to give back your key. You steal the key!

A week later, you return to the hotel, do not check in, sneak into your old room with your stolen key, and look in the drawer. Your book is still there. Astonishing!

How can that be? Isn't the contents of a hotel room drawer inaccessible if you haven't rented the room?

這個作者回答的相當生動,當然後面也補充了一些技術細節,有興趣的可以到這裡參考!

參考文章:


... ...

6/20/2011

[C++] 搞懂複雜變數的簡單規則 - Right-Left Rule

雖然平常寫程式的時候不太會用到那麼複雜的變數,但是這東西考試倒是蠻喜歡考的,靠著 Right-Left Rule 在複雜的變數也可以被輕鬆破解!

底下為轉文,原文在 C Right-Left Rule (Rick Ord's CSE 30 - UC San Diego)

The "right-left" rule is a completely regular rule for deciphering C
declarations.  It can also be useful in creating them.

First, symbols.  Read

     *  as "pointer to"   - always on the left side
     []  as "array of"   - always on the right side
     ()  as "function returning"  - always on the right side

as you encounter them in the declaration.

STEP 1
------
Find the identifier.  This is your starting point.  Then say to yourself,
"identifier is."  You've started your declaration.

STEP 2
------
Look at the symbols on the right of the identifier.  If, say, you find "()"
there, then you know that this is the declaration for a function.  So you
would then have "identifier is function returning".  Or if you found a 
"[]" there, you would say "identifier is array of".  Continue right until
you run out of symbols *OR* hit a *right* parenthesis ")".  (If you hit a 
left parenthesis, that's the beginning of a () symbol, even if there
is stuff in between the parentheses.  More on that below.)

STEP 3
------
Look at the symbols to the left of the identifier.  If it is not one of our
symbols above (say, something like "int"), just say it.  Otherwise, translate
it into English using that table above.  Keep going left until you run out of
symbols *OR* hit a *left* parenthesis "(".  

Now repeat steps 2 and 3 until you've formed your declaration.  Here are some
examples:

     int *p[];

1) Find identifier.          int *p[];
                                  ^
   "p is"

2) Move right until out of symbols or left parenthesis hit.
                             int *p[];
                                   ^^
   "p is array of"

3) Can't move right anymore (out of symbols), so move left and find:
                             int *p[];
                                 ^
   "p is array of pointer to"

4) Keep going left and find:
                             int *p[];
                             ^^^
   "p is array of pointer to int". 
   (or "p is an array where each element is of type pointer to int")

Another example:

   int *(*func())();

1) Find the identifier.      int *(*func())();
                                    ^^^^
   "func is"

2) Move right.               int *(*func())();
                                        ^^
   "func is function returning"

3) Can't move right anymore because of the right parenthesis, so move left.
                             int *(*func())();
                                   ^
   "func is function returning pointer to"

4) Can't move left anymore because of the left parenthesis, so keep going
   right.                    int *(*func())();
                                           ^^
   "func is function returning pointer to function returning"

5) Can't move right anymore because we're out of symbols, so go left.
                             int *(*func())();
                                 ^
   "func is function returning pointer to function returning pointer to"

6) And finally, keep going left, because there's nothing left on the right.
                             int *(*func())();
                             ^^^
   "func is function returning pointer to function returning pointer to int".


As you can see, this rule can be quite useful.  You can also use it to
sanity check yourself while you are creating declarations, and to give
you a hint about where to put the next symbol and whether parentheses
are required.

Some declarations look much more complicated than they are due to array
sizes and argument lists in prototype form.  If you see "[3]", that's
read as "array (size 3) of...".  If you see "(char *,int)" that's read
as "function expecting (char *,int) and returning...".  Here's a fun
one:

                 int (*(*fun_one)(char *,double))[9][20];

I won't go through each of the steps to decipher this one.

Ok.  It's:

     "fun_one is pointer to function expecting (char *,double) and 
      returning pointer to array (size 9) of array (size 20) of int."

As you can see, it's not as complicated if you get rid of the array sizes
and argument lists:

     int (*(*fun_one)())[][];

You can decipher it that way, and then put in the array sizes and argument
lists later.

Some final words:

It is quite possible to make illegal declarations using this rule,
so some knowledge of what's legal in C is necessary.  For instance,
if the above had been:

     int *((*fun_one)())[][];

it would have been "fun_one is pointer to function returning array of array of
                                          ^^^^^^^^^^^^^^^^^^^^^^^^
pointer to int".  Since a function cannot return an array, but only a 
pointer to an array, that declaration is illegal.


Illegal combinations include:

  []() - cannot have an array of functions
  ()() - cannot have a function that returns a function
  ()[] - cannot have a function that returns an array

In all the above cases, you would need a set of parens to bind a *
symbol on the left between these () and [] right-side symbols in order
for the declaration to be legal.

Here are some legal and illegal examples:

int i;                  an int
int *p;                 an int pointer (ptr to an int)
int a[];                an array of ints
int f();                a function returning an int
int **pp;               a pointer to an int pointer (ptr to a ptr to an int)
int (*pa)[];            a pointer to an array of ints
int (*pf)();            a pointer to a function returning an int
int *ap[];              an array of int pointers (array of ptrs to ints)
int aa[][];             an array of arrays of ints
int af[]();             an array of functions returning an int (ILLEGAL)
int *fp();              a function returning an int pointer
int fa()[];             a function returning an array of ints (ILLEGAL)
int ff()();             a function returning a function returning an int
                                (ILLEGAL)
int ***ppp;             a pointer to a pointer to an int pointer
int (**ppa)[];          a pointer to a pointer to an array of ints
int (**ppf)();          a pointer to a pointer to a function returning an int
int *(*pap)[];          a pointer to an array of int pointers
int (*paa)[][];         a pointer to an array of arrays of ints
int (*paf)[]();         a pointer to a an array of functions returning an int
                                (ILLEGAL)
int *(*pfp)();          a pointer to a function returning an int pointer
int (*pfa)()[];         a pointer to a function returning an array of ints
                                (ILLEGAL)
int (*pff)()();         a pointer to a function returning a function
                                returning an int (ILLEGAL)
int **app[];            an array of pointers to int pointers
int (*apa[])[];         an array of pointers to arrays of ints
int (*apf[])();         an array of pointers to functions returning an int
int *aap[][];           an array of arrays of int pointers
int aaa[][][];          an array of arrays of arrays of ints
int aaf[][]();          an array of arrays of functions returning an int
                                (ILLEGAL)
int *afp[]();           an array of functions returning int pointers (ILLEGAL)
int afa[]()[];          an array of functions returning an array of ints
                                (ILLEGAL)
int aff[]()();          an array of functions returning functions
                                returning an int (ILLEGAL)
int **fpp();            a function returning a pointer to an int pointer
int (*fpa())[];         a function returning a pointer to an array of ints
int (*fpf())();         a function returning a pointer to a function
                                returning an int
int *fap()[];           a function returning an array of int pointers (ILLEGAL)
int faa()[][];          a function returning an array of arrays of ints
                                (ILLEGAL)
int faf()[]();          a function returning an array of functions
                                returning an int (ILLEGAL)
int *ffp()();           a function returning a function
                                returning an int pointer (ILLEGAL)


... ...