淺談C說話中的強符號、弱符號、強援用和弱援用。本站提示廣大學習愛好者:(淺談C說話中的強符號、弱符號、強援用和弱援用)文章只能為提供參考,不一定能成為您想要的結果。以下是淺談C說話中的強符號、弱符號、強援用和弱援用正文
起首我表現很喜劇,在看《法式員的自我教養--鏈接、裝載與庫》之前我竟不曉得C有強符號、弱符號、強援用和弱援用。在看到3.5.5節弱符號和強符號時,我感到有些迷惑,所以寫下此篇,願望能和異樣感到的同伙交換也願望高人指導。
起首我們看一下書中關於它們的界說。
引入場景:(1)文件A中界說並初始化變量i(int i = 1), 文件B中界說並初始化變量i(int i = 2)。編譯鏈接A、B時會報錯b.o:(.data+0x0): multiple definition of `i';a.o:(.data+0x0): multiple definition of `i'。(2)在文件C中界說並初始化兩個變量i(int i = 1; int i = 2), 編譯鏈接時會報錯c.c:2:5: error: redefinition of ‘i'; c.c:1:5: note: previous definition of ‘i' was here。
強符號:像場景中如許的符號界說被稱為強符號,關於C/C++來講,編譯器默許函數和初始化的全局變量為強符號。
弱符號:接上文,為初始化的全局變量為弱符號。
編譯器關於強弱符號的規矩有:(1)強符號不許可屢次界說,但強弱可以共存;(2)強弱共存時,強籠罩弱;(3)都是弱符號時,選擇占用空間最年夜的,如選擇 double類型的而不選擇int類型的。
由以上界說所以有我之前沒有想到的場景:
代碼a.c:
1 int i = 2;
代碼b.c:
#include<stdio.h>
int i;
int main(int argc, char** argv)
{
printf("i = %d\n", i);
return 0;
}
編譯文件a和b並鏈接,成果輸入i為2而不是0。
而且在統一個文件中界說但未初始化兩個雷同的變量不會報錯,只要在應用變量時才會報錯。
關於GCC編譯器來講,還許可應用__attribute__((weak))來將強符號界說為弱符號,所已有
代碼c.c
#include<stdio.h>
__attribute__((weak)) int i = 1;
int main(int argc, char** argv)
{
printf("i = %d\n", i);
return 0;
}
成果i的輸入仍未2而不是1。
那末關於函數而言是否是也如許呢?先不看函數,而是先看由強弱符號而進一步引入的強弱援用。書中關於強弱援用的概述是關於強援用若不決義則鏈接時確定會報錯,而關於弱援用則不會報錯,鏈接器默許其為0(這一點關於函數好懂得,即函數符號所代表進口地址為0;關於變量就要留意了,既然是援用那天然就是地址了,所以同函數一樣變量的地址為0而不是變量的值為0)。此時關於強弱援用是否是還沒有甚麼明白的概念呢?究竟甚麼是援用?援用和符號又是甚麼關系?這裡我說一下我的懂得(迎接斧正),在界說和聲明處指定的函數名、變量名即為對應的符號,而在代碼其他處挪用函數或應用變量時,則把函解釋和變量名看做援用,如許一來符號和援用在代碼層面上其實就是一個器械,只是依據情況而叫法分歧罷了。那末強符號對應強援用,弱符號對應弱援用。
有下面的強弱援用的特色可看出,當一個函數為弱援用時,不論這個函數有無界說,鏈接時都不會報錯,並且我們可以依據斷定函數名能否為0來決議能否履行這個函數。如許一來,包括這些函數的庫便可以以模塊、插件的情勢和我們的援用組合一路,便利應用和卸載,而且因為強符號可以籠罩弱符號和強弱符號與強弱援用的關系可知,我們本身界說函數可以籠罩庫中的函數,何等美好。
先看依據前提斷定能否履行函數:
代碼d.c
#include<stdio.h>
void func()
{
printf("func()#1\n");
}
代碼e.c
#include<stdio.h>
__attribute__((weak)) void func();
int main(int argc, char** argv)
{
if (func)
func();
return 0;
}
編譯d.c,cc -c d.c 輸入d.o;編譯e.c並鏈接d.o,cc d.o e.c -o e輸入可履行文件e,運轉e正常履行函數func。編譯e.c但不鏈接d.o,此時其實不會報錯,只不外func不會履行,由於沒有它的界說所以if(func)為假。
再看函數籠罩:
代碼f.c
#include<stdio.h>
__attribute__((weak)) void func()
{
printf("func()#1\n");
}
代碼g.c
#include<stdio.h>
void func()
{
printf("func()#2\n");
}
int main(int argc, char** argv)
{
func();
return 0;
}
~
編譯鏈接,構造輸入"func()#2"。
以上可以解釋函數和變量是堅持分歧的,其實對應變量也能夠像應用函數那樣先斷定再應用,只不外不是斷定變量的值而是變量的地址,如
代碼v1.c
int i = 2;
代碼v2.c
#include<stdio.h>
__attribute__((weak)) extern int i;
int main(int argc, char** argv)
{
if (&i)
printf("i = %d\n", i);
return 0;
}
~
編譯並鏈接v1時,輸入2;編譯但不鏈接v1時無輸入。如許做時要分清界說和聲明的差別,__attribute__((weak)) int i 是界說變量並轉換為弱符號,如許i是分派了空間的,而__attribute__((weak)) extern int i 則將本來界說的變量i由強符號轉換為弱符號,招致應用i時不是強援用而是弱援用。不外固然變量可以這麼做但沒有函數那樣成心義。
下面關於強弱援用仍然應用的是GCC供給的__attribute__((weak)),而書中還提到了__attribute__((weakref)),後者貌似更能表現“援用”這一症結詞。而我之所以應用前者來引見強弱援用,是由於我對關於強弱符號與強弱援用對應關系的懂得。關於__attribute__((weakref))的應用辦法,這裡引見一種(二者都有分歧的應用辦法)。
代碼a.c
#include<stdio.h>
void bar()
{
printf("foo()\n");
}
代碼b.c
#include<stdio.h>
static void foo() __attribute__((weakref("bar")));
int main(int argc, char** argv)
{
if (foo)
foo();
return 0;
}
留意函數foo的static潤飾符,沒有的話會報錯,如許將函數foo限制在只要本文件內可以使用。
好了,夜已深,寫的有點紛亂,我也紛亂了。