• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            woaidongmao

            文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
            隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
            數(shù)據(jù)加載中……

            extern數(shù)組與extern指針

            數(shù)組名代表了存放該數(shù)組的那塊內存,它是這塊內存的首地址。這就說明了數(shù)組名是一個地址,而且,還是一個不可修改的常量,完整地說,就是一個地址常量。數(shù)組名跟枚舉常量一樣,都屬于符號常量。數(shù)組名這個符號,就代表了那塊內存的首地址。注意了!不是數(shù)組名這個符號的值是那塊內存的首地址,而是數(shù)組名這個符號本身就代表了首地址這個地址值,它就是這個地址。這就是數(shù)組名屬于符號常量的意義所在。由于數(shù)組名是一種符號常量,它是一個右值,而指針,作為變量,卻是一個左值,一個右值永遠都不是左值,那么,數(shù)組名永遠都不會是指針!

            對于這段話我是這么理解的:數(shù)組名在經過編譯之后將變成一個數(shù)值,這個數(shù)值就是該數(shù)組的首地址。由于數(shù)組名是一個地址,那么把它賦給一個指針變量也就不足為奇了。

            例如有定義

            char a[14];

            char * p;

            char * q;

            void foo(char * pt)

            {

            };

            考慮以下幾種賦值:

            p=a;//合法,將一個地址賦給一個指針變量;

            q=p;//合法,將一個指針變量的值賦給另一個指針變量;

            a=p;//非法,a是數(shù)組名即地址,不是一個變量,不可被賦值(也就是上文中說的"數(shù)組名是右值"

            再看這幾種調用:

            foo(a);//將一個地址作為參數(shù)傳入函數(shù),函數(shù)中用一個指針變量接收這個地址值

            foo(p);//將一個指針變量的值傳入函數(shù)(也是一個地址),函數(shù)中用一個指針變量接收這個地址

             

            可以看出許多時候數(shù)組名和指針可以等同地看待,而c也把它們看作是兼容的類型對待,這就是為什么我那個錯誤的聲明不被編譯器在語法檢查的時候“喀嚓”的原因。

             

            關于extern的作用,許多地方都有說明,例如可以在c++里進行c格式函數(shù)的聲明,可以聲明一個變量或函數(shù)是外部變量或外部函數(shù);我們這里要討論的是外部變量的聲明。被extern修飾的全局變量不被分配空間,而是在連接的時候到別的文件中通過查找索引定位該全局變量的地址。

             

            有了這些基礎后,我們現(xiàn)在正式開始研究extern 數(shù)組和extern 指針的問題:

             

            首先在一個.c文件中有如下定義:

            char a[]={1,2,3,4};

            分析:這是一個數(shù)組變量的定義,編譯器將為這個數(shù)組分配4字節(jié)的空間,并且建立一個索引,把這個數(shù)組名、數(shù)組類型和它被分配的空間首地址對應起來。它被編譯之后生成一個中間文件

            然后我們在另一個.c文件中分別以不同的形式進行聲明:

            (1) extern char a[];

            分析:這是一個外部變量的聲明,它聲明了一個名為a的字符數(shù)組,編譯器看到這個聲明就知道不必為這個變量分配空間,這個.c文件中所有對數(shù)組a的引用都化為一個不包含類型的標號,具體地址的定位留給連接器完成。編譯完成之后也得到一個中間文件,連接器遍歷這個文件,發(fā)現(xiàn)有未經定位的標號,于是它搜索其他中間文件,試圖尋找到一個匹配的空間地址,在此例中無疑連接器將成功地尋找到這個地址并將此中間文件中所有的這個標號替換為連接器所尋找到的地址,最終生成的可執(zhí)行文件中,所有曾經的標號都應當已經被替換為地址。這是一個正常工作過程,連接出來的可執(zhí)行文件至少在對于該數(shù)組的引用部分將工作得很好。

            (2) extern char * a;

            分析:這是一個外部變量的聲明,它聲明了一個名為a的字符指針,編譯器看到這個聲明就知道不必為這個指針變量分配空間,這個.c文件中所有對指針a的引用都化為一個不包含類型的標號,具體地址的定位留給連接器完成。編譯完成之后仍然得到一個中間文件,連接器遍歷這個文件,發(fā)現(xiàn)有未經定位的標號,于是它搜索其他中間文件,試圖尋找到一個匹配的空間地址,經過一番搜索,找到了一個分配過空間的名為a的地方(也就是我們先定義的那個字符數(shù)組),連接器并不知道它們的類型,僅僅是發(fā)現(xiàn)它們的名字一樣,就認為應該把extern聲明的標號連接到數(shù)組a的首地址上,因此連接器把指針a對應的標號替換為數(shù)組a的首地址。這里問題就出現(xiàn)了:由于在這個文件中聲明的a是一個指針變量而不是數(shù)組,連接器的行為實際上是把指針a自身的地址定位到了另一個.c文件中定義的數(shù)組首地址之上,而不是我們所希望的把數(shù)組的首地址賦予指針a(這很容易理解:指針變量也需要占用空間,如果說把數(shù)組的首地址賦給了指針a,那么指針a本身在哪里存放呢?)。這就是癥結所在了。所以此例中指針a的內容實際上變成了數(shù)組a首地址開始的4字節(jié)表示的地址(如果在16位機上,就是2字節(jié))。本例中指針a的初值將會是0x0a090807little endian),顯然不是我們的期望值,所以運行會出錯也就理所應當了。

            ?

            幾點細節(jié):我們發(fā)現(xiàn),使用extern修飾的變量在連接的時候只找尋同名的標號,不檢查類型,例如如果我們定義的甚至不是一個變量而是一個全局的函數(shù),比如去掉定義

            char a[]={....};

            代之以

            void a(){};

            連接器居然也會連接通過。

            實例如下:

            比如在a.c文件中有這樣一段代碼

             

            int g_i[] = {1, 2, 3, 4};

            extern void testdotp();

             

            void main(void)

            {

               int i = 0;

               int num = 0;

               num = sizeof(g_i) / sizeof(int);

               for (i = 0; i < num; i++)

               {

                  printf("g_i[%d] = %d ", i, g_i[i]);

               }

               printf("\n");

               testdotp();

            }

             

            而在b.c中的代碼如下:

            extern int *g_i;

             

            void testdotp()

            {

               printf("*(&g_i + 2) = %d\n", *(&g_i + 2));

               printf("&g_i = %d\n", &g_i);

               printf("&g_i + 1= %d\n", &g_i + 1);

               printf("g_i = %d\n", g_i);

               printf("g_i + 1 = %d\n", g_i + 1);

            }

            運行結果為

            g_i[0] = 1 g_i[1] = 2 g_i[2] = 3 g_i[3] = 4

            *(&g_i + 2) = 3

            &g_i = 4344368

            &g_i + 1= 4344372

            g_i = 1

            g_i + 1 = 5

             

            分析如下:

            因為b.c文件中g_i變量的地址是a.c文件中g_i數(shù)組的首地址,故g_i的值為g_i[0]的值,&g_i的值為g_i地址的首地址。

             

            *(&g_i + 2)的值:&g_i的值為g_i數(shù)組的首地址,(&g_i + 2)就為數(shù)組g_i3個元素的地址,*(&g_i + 2)就為第2個元素的值,即3

             

            &g_i + 1:由于&g_i的值為g_i數(shù)組首地址,由于在32位機上運行,故&g_i + 1&g_i基礎上加上4個字節(jié)

            g_i + 1:由于g_i是一個指針變量,g_i變量內存放的是地址,又因為g_i的值為1,而g_i + 1就為1 + 4的單元的內存空間(32位機上),故g_i + 15

             

            posted on 2009-11-17 10:53 肥仔 閱讀(2064) 評論(0)  編輯 收藏 引用 所屬分類: C++ 基礎

            久久久久久久久久久久久久| 国产欧美久久久精品影院| 久久99国产精品久久久| 久久香蕉国产线看观看乱码| 2021国产精品午夜久久 | 丰满少妇人妻久久久久久4| 久久国语露脸国产精品电影| 成人精品一区二区久久久| 人妻少妇久久中文字幕一区二区| 久久久久久久综合狠狠综合| 久久久久久亚洲Av无码精品专口| 久久水蜜桃亚洲av无码精品麻豆| 色偷偷88欧美精品久久久| 国产精品99久久久精品无码| 亚洲国产视频久久| 97精品国产91久久久久久| 丁香色欲久久久久久综合网| 亚洲人成网亚洲欧洲无码久久| 国产激情久久久久久熟女老人 | 久久精品免费观看| 欧美久久久久久精选9999| 99久久99这里只有免费费精品| 狠狠综合久久综合88亚洲| Xx性欧美肥妇精品久久久久久| 国产精品久久久久久久久久影院| 91精品婷婷国产综合久久| 久久精品国产亚洲av水果派| 久久99精品久久久久久水蜜桃 | 久久妇女高潮几次MBA| 亚洲国产精品综合久久一线| 国内精品伊人久久久久妇| 中文字幕乱码人妻无码久久| 99久久精品国产毛片| 人人狠狠综合久久亚洲| 久久九九精品99国产精品| 久久精品亚洲乱码伦伦中文| 久久久久女人精品毛片| 久久久久亚洲AV无码专区首JN | 亚洲av日韩精品久久久久久a| 久久狠狠一本精品综合网| 亚洲一区中文字幕久久|