程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 舉例講授C說話鏈接器的符號解析機制

舉例講授C說話鏈接器的符號解析機制

編輯:關於C++

舉例講授C說話鏈接器的符號解析機制。本站提示廣大學習愛好者:(舉例講授C說話鏈接器的符號解析機制)文章只能為提供參考,不一定能成為您想要的結果。以下是舉例講授C說話鏈接器的符號解析機制正文


1. 符號分類
(1)全局符號:非靜態全局變量,非靜態函數
(2)內部符號:界說於其它模塊,而被本模塊援用的全局變量和函數
(3)當地符號:靜態變量(包含全局和部分),靜態函數
關於靜態部分變量,編譯器會為其生成獨一的名字。如x.fun1,x.fun2。當地符號對鏈接器來講是弗成見的。
2. 符號決定
當編譯器碰到一個不是本模塊界說的符號時,會假定該函數由其它模塊界說,並生成一個鏈接器符號表條目,交由鏈接器處置。假如鏈接器在它的任何輸出模塊都沒有找到該符號,會給出一個相似undefined reference to 'xxx'的鏈接毛病。而假如鏈接器在輸出模塊中找到了一個以上的內部符號界說,這個時刻就須要鏈接器停止符號決定,鏈接器對多個內部符號界說能夠其實不報錯乃至正告,而是依照它的規矩去選擇個中一個符號界說。
鏈接器將各個模塊輸入的全局符號,分類為強符號和弱符號:
(1)強符號:函數和已初始化的全局變量
(2)弱符號:為初始化全局變量
依據強弱符號的界說,鏈接器依照上面的規矩處置多重界說的符號:
規矩1:不許可有多個強符號界說
規矩2:假如有一個強符號和多個弱符號,那末選擇強符號
規矩3:假如有多個弱符號,那末從這些弱符號當選擇sizeof年夜的誰人,假如年夜小雷同,則選擇先鏈接的誰人
下面的規矩是許多鏈接毛病的本源,由於編譯器在決定時能夠默默地替你作出了決議,你其實不知曉。依據下面的規矩,可以引出上面幾個經典例子:
例1:

// in lib1.c
int x;
void f()
{
  x = 1235;
}

// in main1.c
#include<stdio.h>
void f(void);

int x = 1234;

int main(void)
{
  f();
  printf("x=%d\n", x);
  return 0;
}

下面的代碼中,main函數printf輸入: x=1235。由於鏈接器經由過程規矩2決定符號x的界說為main.c中的強符號界說,而lib.c的作者其實不知情,他對x的應用和修正影響到了main.c。這類交互修正,互相影響將會很龐雜,由於年夜家都認為本身在做對的工作,在用對的變量。而全部決定進程,鏈接器悄無聲氣地完成了。
例2:

// in lib2.c
double x;
void f()
{
  x = -0.0;
}

// in main2.c
#include<stdio.h>
void f(void);

int x = 1234;
int y = 1235;

int main()
{
  f();
  printf("x=0x%x y=0x%x \n", x, y);
  return 0;
}

這類情形下,法式獲得輸入: x=0x0 y=0x80000000,而鏈接器(gcc ld)也終究給出一條正告:

ld: warning: tentative definition of '_x' with size 8 from 'obj/Debug/lib2.o' is being replaced by real definition of smaller size 4 from 'obj/Debug/main2.o'


鏈接器決定的是符號地址,而相鄰的全局變量能夠在.data段中的內存地址也相鄰,是以也就激發了更龐雜的成績。這一點和棧溢出很像,然則比棧溢出更龐雜,由於成績出在多個模塊之間,而不是在一個函數外部。
例3:

// in lib3.c
struct
{
  int a;
  int b;
} x;

void f()
{
  x.a = 123;
  x.b = 456;
  printf("in f(): sizeof(x)=%d, (&x)=0x%08x\n", sizeof(x), &x);
}

// in main3.c
#include<stdio.h>
void f(void);

int x;
int y;

int main()
{
  f();
  printf("in main(): sizeof(x)=%d, (&x)=0x%08x, (&x)=0x%08x, x=%d,y=%d \n", sizeof(x), &x, &y, x, y);
  return 0;
}

法式輸入:

in f(): sizeof(x)=8, (&x)=0x02489018
in main(): sizeof(x)=4, (&x)=0x02489018, (&y)=0x02489020, x=123,y=0

一直記住,內部符號決定的是地址,是以不管lib3.c和main3.c中,符號x地址都是獨一的,不管其被界說了幾回。其次sizeof是編譯器決定,與鏈接有關,編譯器只看獲得本模塊的界說或聲明。最初,因為符號x決定到lib3.c中的x,其size是8,是以main3.c中的y的地址比x年夜8,這是由鏈接器將lib3.o和main3.o歸並後填入可履行文件的.data段的。是以y是有關變量,被初始化為0,留意和例2的差別。
3. 總結
因為符號決定輕易激發的各種成績,我們在寫C的時刻應留意:
盡可能用static屬性隱蔽變量和函數在模塊內的聲明,就像在C++中盡可能用private掩護類公有成員一樣。
少界說弱符號,盡可能初始化全局變量,如許鏈接器會依據規矩1給出多個符號界說的毛病。
為鏈接器設置需要選項,如gcc的 -fno-common,如許在碰到多重符號界說時,鏈接器會給出正告。
4. C++的符號決定
C++其實不支撐強弱符號同時存在,一切符號都只能有一個界說(函數重載經由過程改寫函數符號來確保其獨一),是以在很年夜水平上防止了C中的鏈接器困擾。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved