?
14.8??變量類型
ARM?C編譯器支持基本的數(shù)據(jù)類型:char、short、int、long?long、float和double。表14.2說明了armcc對C語言所使用的數(shù)據(jù)類型的映射。
表14.2 C編譯器數(shù)據(jù)類型映射
C數(shù)據(jù)類型 |
表示的意義 |
char |
無符號8位字節(jié)數(shù)據(jù) |
short |
有符號16位半字數(shù)據(jù) |
int |
有符號32位字數(shù)據(jù) |
long |
有符號32位字數(shù)據(jù) |
long?long |
有符號64位雙字數(shù)據(jù) |
ARM指令集中,無論是數(shù)據(jù)處理指令還是數(shù)據(jù)加載/存儲指令,處理的數(shù)據(jù)類型不同,指令的執(zhí)行效率是不一樣的。本章將詳細討論,如何在程序中為變量分配合理的數(shù)據(jù)類型,來提高代碼的執(zhí)行效率。
14.8.1??局部變量
ARM屬于RISC的體系結(jié)構(gòu),所有大多數(shù)的數(shù)據(jù)處理都是在32位的寄存器中進行的?;谶@個原因,局部變量應(yīng)盡可能使用32位數(shù)據(jù)類型int或long。
注意 |
一些情況下不得不使用char或short類型,例如要使用char或short類型的數(shù)據(jù)溢出指令時歸零特性時,如模運算255+1=0,就要使用char類型。 |
為了說明局部變量類型的影響,先來看一個簡單的例子。
char?charinc?(char?a)
{
return?a?+?1;
}
編譯出的結(jié)果如下。
charinc
??????????ADD??????a1,a1,#1
??????????AND??????a1,a1,#&ff
??????????MOV??????pc,lr
再把上面的程序段中變量a聲明位int型,代碼如下。
int?wordinc?(int?a)
{
return?a?+?1;
}
比較一下編譯器輸出結(jié)果。
wordinc
??????????ADD??????a1,a1,#1
??????????MOV??????pc,lr
分析上面的兩段代碼不難發(fā)現(xiàn),當把變量聲明為char型時,編譯器增加了額外的ADD指令來保證其范圍在0~255之間。
14.8.2??有符號數(shù)和無符號數(shù)
上一節(jié)討論了對于局部變量和函數(shù)參數(shù),使用int型比使用char或short型要好。本節(jié)將對程序中的有符號整數(shù)(signed?int)和無符號整數(shù)(unsigned?int)的執(zhí)行效率進行分析比較。
首先來看上一節(jié)的例子,如果將變量指定為有符號的半字類型(編譯器默認short型為有符號類型),程序的源代碼如下。
short?shortinc?(short?a)
{
return?a?+?1;
}
編譯后的結(jié)果如下。
shortinc
??????????ADD??????a1,a1,#1
??????????MOV??????a1,a1,LSL?#16
??????????MOV??????a1,a1,ASR?#16
??????????MOV??????pc,lr
分析發(fā)現(xiàn),該結(jié)果比使用int型的變量多增加了兩條指令(LSL和ASR)。編譯器先將變量左移16位,然后右移16位,以實現(xiàn)一個16位符號擴展。右移是符號位擴展移位,它復(fù)制了符號位來填充高16位。
通常情況下,如果程序中只有加法、減法和乘法,那么有符號和無符號數(shù)的執(zhí)行效率相差不大。但是,如果有了除法,情況就不一樣了。詳細內(nèi)容可參加除法運算優(yōu)化一節(jié)。
?
14.8.3??全局變量
1.邊界對齊
對于RISC體系結(jié)構(gòu)的處理器來說,訪問邊界對齊的數(shù)據(jù)要比訪問非對齊的數(shù)據(jù)更高效。表14.3顯示了ARM結(jié)構(gòu)下各數(shù)據(jù)類型所占的字節(jié)數(shù)。
表14.3 各數(shù)據(jù)類型所占字節(jié)數(shù)
C數(shù)據(jù)類型 |
所占字節(jié)數(shù) |
char,singed?char,unsigned?char |
1 |
short,unsigned?short |
2 |
int,unsigned?int,long,unsigned?long |
4 |
float |
4 |
double |
4 |
long?long |
4 |
變量定義雖然很簡單,但是也有很多值得注意的地方。先看下面的例子。
定義1:
char??a;
short??b;
char??c;
int??d;
定義2:
char??a;
char??c;
short??b;
int??d;
這里定義的4個變量形式都一樣,只是次序不同,卻導(dǎo)致了在最終映像中不同的數(shù)據(jù)布局,如同14.1所示,其中pad為無意義的填充數(shù)據(jù)。
圖14.1??變量在數(shù)據(jù)區(qū)里的布局
從圖中可以看出,第二種方式節(jié)約了更多的存儲器空間。
由此可見,在變量聲明的時候需要考慮怎樣最佳的控制存儲器布局。當然,編譯器在一定程度上能夠優(yōu)化這類問題,但最好的方法還是在編譯的時候把所有相同類型的變量放在一起定義。
2.訪問外部變量
首先來看一個例子。下面的例子定義了一些全局變量,在main(?)中為這些變量賦值并將其打印輸出。
/************
*?access.c?*
************/
#include?<stdio.h>
char?tx;
char?rx;
char?byte;
char?c;
unsigned?state;
unsigned?flags;
int?main?()
{?tx?=?1;
rx?=?2;
byte?=?3;
c?=?4;
state?=?5;
flags?=?6;
printf("%u?%u?%u?%u?%u?%un",?tx,?rx,?byte,?c,?state,?flags);
return?0;
}
使用armcc編譯,生成的代碼大小如下。
C$$code?132
C$$data?12
如果將全局變量聲明為extern,變量的定義在其他文件中,那么生成的代碼量將有所增加。
將全局變量聲明為extern,生成的代碼大小如下。
C$$code?168
C$$data?12
這是因為當將變量聲明為extern后,每次訪問變量編譯器都將從內(nèi)存重新加載,而不是使用內(nèi)存偏移,直接訪問。
下圖顯示編譯器對聲明為extern變量的訪問。
解決的辦法是將要從外部引用的extern變量定義在一個結(jié)構(gòu)體中。在程序中通過結(jié)構(gòu)體訪問外部變量。具體用法如下例所示。
/*************
*?globals.h?*
*************/
/*?DECLARATIONS?of?globals?-?included?in?all?sources?*/
#ifdef?__arm
struct?globs
{?char?tx;
char?rx;
圖14.2??對extern變量的訪問
?
char?byte;
char?c;
unsigned?state;
unsigned?flags;
};
extern?struct?globs?g;
#define?tx?g.tx
#define?rx?g.rx
#define?byte?g.byte
#define?c?g.c
#define?state?g.state
#define?flags?g.flags
#else
extern?char?tx;
extern?char?rx;
extern?char?byte;
extern?char?c;
extern?unsigned?state;
extern?unsigned?flags;
#endif
/*************
*?globals.c?*
*************/
/*?DEFINITIONS?of?globals?-?single?source?file?*/
#ifdef?__arm
#?include?"globals.h"
struct?globs?g;
#else
char?tx;
char?rx;
char?byte;
char?c;
unsigned?state;
unsigned?flags;
#endif
/************
*?access.c?*
************/
#include?<stdio.h>
#include?"globals.h"
int?main?()
{tx?=?1;
rx?=?2;
byte?=?3;
c?=?4;
state?=?5;
flags?=?6;
printf("%u?%u?%u?%u?%u?%un",?tx,?rx,?byte,?c,?state,?flags);
return?0;
}
將變量定義在結(jié)構(gòu)體內(nèi)有以下幾點好處。
·??全局變量使用更小的內(nèi)存空間。(沒有使用結(jié)構(gòu)體占有24字節(jié),而使用結(jié)構(gòu)體之后只占有12字節(jié))
·??全局變量被放置在ZI段而不是RW段,這樣就減少了ROM映像文件的大小。