C語言extern引用AT&T彙編中的變數,任意轉換型別

今天研究出了一個小問題,在C語言裡引用匯編的變數,會是什麼結果,彙編中的變數沒有像C語言中int型別那樣的型別約束,可以把資料當作任何型別處理,那麼傳到C語言中我們應該當作什麼型別處理呢。

換句話說,在彙編裡定義變數var,在C語言裡引用,我們肯定要用extern宣告var外部變數,那麼extern後面接什麼型別?難道是extern int var麼?還是 extern short var

例項:

注意:混合編譯的方法,gcc test.s main.c 這樣直接用gcc處理彙編和C語言檔案就可以自動搞定。

一、

彙編程式:(是的,就這麼短小)

.global var  #注意現在的彙編器不再要求被C語言引用的變數名前加下劃線
.data
var:
.fill 10,4,9 #填充10個單元,每個單元4位元組,每個單元的值為9

C語言:

#include <stdio.h> 
extern int var;  //現在的C語言編譯器引用匯編變數可以使用和彙編變數同樣的名字,寫成_var反而會錯誤
void main() 
{ 
printf("sizeof(var)=%d\n",sizeof(var));
printf("%d\n", var); 
}

程式的輸出為

sizeof(int)=4
9

解釋很簡單,

如上圖,我只畫了四個位元組,我填充了10個單元的,每單元佔用4位元組,意思就是每個9佔四個位元組。如圖,然後C語言裡宣告為extern int var;就是把前四個位元組看作一個int變數取出來,當然是9了。

二、

保持C語言程式和上一個一樣,

extern int var;

把彙編語句改成

.fill 10,2,9 #10個單元,每個單元填入9,每單元大小為2B

這就意味著每個單元2位元組,每個單元填一個9,我們不改變C語言語句,那麼仍將把var看作int型4位元組變數,依然是一次取出前4個位元組作為var的值。那我們推測一下結果會是什麼。

首先每個9佔用兩位元組,記憶體圖應該是這樣的,(只畫了4位元組,後面沒畫)

一次性取出4個位元組,就是0x00090009,這玩意兒拿到windows自帶計算器裡算出來是是十進位制的589833,這就是我們的預測值。讓我們看下程式結果是不是和我們猜的一樣。

沒錯吧,這就是說我們在彙編裡定義的變數,來到C語言裡可以任意宣告為任意型別,從sizeof的輸出也能看出,我們把var宣告成多大,它就是多大。

三、

我們再試試別的,彙編語句保持和上一個一樣,依然是

.fill 10,2,9 #10個單元,每個單元填入9,每單元大小為2B

我們改變C語言語句,把var宣告成

extern long long var; //8位元組整型

當然,輸出語句稍微變下,因為是long long型整數,所以要這麼輸出printf(“%lld\n”, var);

#include <stdio.h> 
extern long long var;   
void main() 
{ 
printf("sizeof(var)=%d\n",sizeof(var));
printf("%lld\n", var); 
}

我們還是事先猜測結果,位元組太多我不畫圖了,按照上面的的思路,這次取出8位元組當作long long變數var的值,那麼應該是0x0009000900090009,化為十進位制是2533313445691401。

看看程式執行結果:

預測正確。

四、

再來,把var宣告為char變數。

#include <stdio.h> 
extern char var;   #宣告為char
void main() 
{ 
printf("sizeof(var)=%d\n",sizeof(var));
printf("%c\n", var); 
}

輸出語句相應的改變成%c

彙編裡面稍微改改填充的數值,因為9是製表符的ASCII碼,輸出了你也看不到。我們改成填充43

.fill 10,2,43 #10個單元,每個單元填入43(記憶體實際儲存按照二進位制來的),每單元大小為2B

注意43的十六進位制表示為0x2b,這是加號“ ”的ASCII碼,

記憶體裡面長成這樣

C語言會認為var是char型的,取出一個位元組當作var的值,也就是把0x2b送給var,我們把var用%c輸出,就是輸出了0x2b這個ASCII碼所代表的字元。就是“ ”號。

五、

好吧,再這麼整下去也沒完了,下面用一個陣列定義來終結此文吧。

組合語言程式碼和上一個一樣,

.fill 10,2,43

C語言修改成:

#include <stdio.h> 
typedef char CHARARR[20]; //CHARARR是一個型別別名,用它定義的每一個變數都是包含20個字元的陣列
extern CHARARR var; //var被定義為CHARARR型別,是一個包含20個元素的char型陣列。
void main() 
{ 
printf("sizeof(var)=%d\n",sizeof(var));
int i=0;
while(i<=19)
{
printf("[%c]", var[i]); //用[]括起來更容易看出輸出現象
i  ;
}
printf("\n");
}

我們讓var成為陣列,含有20個元素,正好把彙編裡面的 10單元x2位元組 共計20個位元組包括了。

然後用迴圈逐個輸出,為什麼要這麼做而不用%s呢,因為陣列裡有很多位元組是0x00,這是字串終止標誌。%s會被打斷的。

結果如下:

可以看到和預計的一樣,20個元素以ASCII碼 0x2b 0x00 0x2b 0x00。。。。這樣的陣列順序挨個輸出。

講完了,C語言和組合語言結合起來是不是強大到了難以想象的地步,隨心所欲操縱記憶體資料。