弱符号與強符号
對于C/C++語言來說,編譯器預設函數和初始化了的全局變量為強符号,未初始化的全局變量為弱符号。我們也可以通過GCC的__attribute__((weak))将一個強符号變為弱符号。強符号和弱符号都是針對定義來說的,不是針對符号的引用。
extern int ext;
int weak; //弱符号
int strong = 1; //強符号
__attribute__((weak)) weak2 = 2;//弱符号
int main()
{
return 0;
}
針對強弱符号的概念,連結器就會按如下規則處理與選擇多次定義的全局符号:
- 規則1:不允許強符号被多次定義(即不同的目标檔案不能有同名的強符号);如果有多個強符号的定義,則編譯器報符号重複定義錯誤。
- 規則2:如果一個符号在某個目标檔案中是強符号,在其它檔案中是弱符号,那麼選擇強符号。
- 規則3:如果一個符号在所有目标檔案中都是弱符号,那麼選擇其中占用空間最大的一個。
弱引用與強引用目前我們看到的對外部目标檔案的符号引用在目标檔案被最終連結成可執行檔案時,它們必須被正确決議,如果沒有找到該符号的定義,連結器就會報符号為定義錯誤,這種被稱為強引用。與之相對應的還有弱引用,在處理弱引用時,如果該符号有定義,則連結器将該符号的引用決議;如果該符号未被定義,則連結器對于該引用不報錯。
我們用下面的代碼來驗證一下:
1.c
#include<stdio.h>
extern int a;
extern void fun();
extern void func();
int main()
{
fun();
func();
printf("1.c a = %d\n", a);
}
2.c
#include<stdio.h>
int a = 5;
void fun()
{
printf("2.c a = %d\n", a);
}
3.c
#include<stdio.h>
int a;
void func()
{
printf("3.c a = %d\n", a);
}
makefile檔案
main: 1.o 2.o 3.o
gcc 1.o 2.o 3.o -o main
1.o: 1.c
gcc -c 1.c
2.o: 2.c
gcc -c 2.c
3.o: 3.c
gcc -c 3.c
clean:
rm 1.o 2.o 3.o
上面是三個檔案中的代碼,在1.c檔案中定義了main函數,讀者可以自己判斷一下1.c檔案中列印的結果。
我們來看一下列印的結果
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiIXZ05WZj91YpB3In5GcucTN3UTM0cTMzIzMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
結果:發現列印的結果都為5,那是怎麼回事呢?
解析
- 在2.c檔案中,我們定義了a變量并為其指派5,此時在2.c檔案中的a為強符号,在3.c檔案雖然我們也定義了a變量,但是沒有顯示的為其賦初值(在3.c中它的值是0)其為弱符号。當我們将其連結為一個可執行檔案時,編譯器在全局表中發現有兩個a變量,它就會進行決策,将強符号(規則2)的a變量當作這一個可執行檔案的a變量,是以a的值5,當func函數執行時,它需要在可執行檔案中查找a的值,此時也就變為了5。
讀者如果想要深入了解可以看之前我寫的一篇部落格解析ELF檔案。