C語言中全局變量的定義與聲明困擾着許多C語言初學者。本文講述了全局變量定義與聲明的用法,而且本為也將闡述這種用法的內在原理。我們先從兩個錯誤例子引入,以下兩個例程都在vc6.0平台上測試。
兩種錯誤例程
1.unresolved external symbol
例子包含兩個C文件(test.c)和(first.c)和一個頭文件(test.h)。下邊具體展示下它們的代碼。
test.h內容
#ifndef _TEST_H
#define _TEST_H
extern int count;
#endif
test.c內容
#include <stdio.h>
#include "test.h"
extern void Fis_Cal(void);
void main(void)
{
Fis_Cal();
printf("the present value of count is %d\n",count);
}
first.c內容
#include <stdio.h>
#include "test.h"
void Fis_Cal(void)
{
printf("the last value of count is %d\n",count);
count = 1;
}
錯誤分析:test.h頭文件中聲明了全局變量count,但是在兩個C文件中都沒有對count進行定義,所以才會出現unresolved external symbol。
一種解決方法:隨便在兩個C文件中加入一句“int count;”就OK了。例如我們加到test.c中,代碼如下。
#include <stdio.h>
#include "test.h"
extern void Fis_Cal(void);
int count;
void main(void)
{
Fis_Cal();
printf("the present value of count is %d\n",count);
}
說明:加入的“int count;”就是對count的定義,默認的將其初始化為0。
結論:這種錯誤原因是“只聲明未定義”。
2.multiply defined symbols found
還是如此,三個文件。但是,兩個C文件與例程一中的文件一樣,改動的只是頭文件。
test.h內容
#ifndef _TEST_H
#define _TEST_H
int count;
#endif
可以看到,與例程一僅僅差了一個“extern”關鍵詞。
錯誤分析:test.h頭文件中定義了全局變量count,但是在兩個C文件都通過“#include "test.h"”這句話對“int count;”進行了引用,所以造成了重復定義的錯誤。
一種解決方法:添加一個“first.h”的頭文件,並且更改first.c的內容,具體更改如下。
first.h內容
#ifndef _FIRST_H
#define _FIRST_H
extern int count;
#endif
first.c內容
#include <stdio.h>
#include "first.h"
void Fis_Cal(void)
{
printf("the last value of count is %d\n",count);
count = 1;
}
說明:經過這樣的修改,原來的test.c中就包含了count的定義,而first.c中就包含了對count的聲明,重復定義錯誤就得到解決。
結論:這種錯誤原因是“多個C程序都包含了定義全局變量的頭文件”。
原理分析
我認為“int count;”是對全局變量的定義,而“extern int count”是對全局變量的聲明,目的是讓其他文件也使用這個全局變量。下邊我們來挖掘全局變量的定義與聲明的內涵。
全局變量要么初始化(非零),要么沒有初始化(為零)。非零時存儲在程序中的data段,零時存儲在程序的bss段。這談了程序(.bin或者.hex)的結構。我再講一下程序的啟動,程序在啟動(boot)過程中,通常都會運行一個叫bootloader的引導程序,這個引導程序干了很多事情,其中有一最重要的任務就是把程序(test段和rodata段)拷貝到內存,還包括data段的拷貝和bss段初始化。我們着重講一下data段的拷貝和bss段初始化。
我們的編譯器會為我們定義的全局變量分配內存(地址),而且給我們的全局變量賦初值(寫內存或清零),以后我們的程序就會根據需要來讀這個全局變量(地址)或者修改這個全局變量(寫內存)。初值為零時就在bss段,這個段初始化代碼會將這部分清零。初值非零時,初始化代碼會將全局變量的初值拷貝到data段。
那么,顯然全局變量的初值只有一個。我們程序中的全局變量的定義就是對全局變量分配內存並賦初值。而全局變量的聲明是為了跨文件使用全局變量的需要,通過"extern"關鍵詞來將全局變量引出。
順便說一下C語言的存儲類說明符,這能幫助我們加深理解。
C語言的存儲類說明符
Auto 只在塊內變量聲明中被允許, 表示變量具有本地生存期。
Extern 出現在頂層或塊的外部變量函數與變量聲明中,表示聲明的對象具有靜態生存期, 連接程序知道其名字。
Static 可以放在函數與變量聲明中,在函數定義時,只用於指定函數名,而不將函數導出到鏈接程序,在函數聲明中,表示其后邊會有定義聲明的函數,存儲類型static.在數據聲明中,總是表示定義的聲明不導出到連接程序關鍵字。
一種更好的聲明與定義方式
test.h內容
#ifndef _TEST_H
#define _TEST_H
#ifdef GLOBALS
int count;
#else
extern int count;
#endif
#endif
test.c內容
#define GLOBALS
#include <stdio.h>
#include "test.h"
extern void Fis_Cal(void);
void main(void)
{
Fis_Cal();
printf("the present value of count is %d\n",count);
}
first.c內容
#include <stdio.h>
#include "test.h"
void Fis_Cal(void)
{
printf("the last value of count is %d\n",count);
count = 1;
}
說明:這種方法可以只定義一個頭文件實現在不同C文件中分別實現定義與聲明。“#define GLOBALS”只在當前定義的test.c文件中有效,所以在test.c中#include "test.h"預處理后,加入的是int count,而first.c中加入的"extern int count;"。其實還有一種書寫方法,也能實現這個效果。
test.h內容
#ifndef _TEST_H
#define _TEST_H
#ifdef GLOBALS
#define EXT
#else
#define EXT extern
#endif
EXT int count;
#endif 。