本文主要講解編譯器的優(yōu)化
以及指針的匯編
編譯器優(yōu)化
設(shè)置
可在項(xiàng)目的BuildSetting->Optimization Level
中找到咸产,一般的優(yōu)化方案選擇FS
(Fastest,Smallest)
案例分析
有以下代碼
int main(int argc, char * argv[]) {
int a = 1;
int b = 2;
}
-
在沒有優(yōu)化情況下的匯編如下
未優(yōu)化匯編 - 將優(yōu)化方案從
None
改成FS
仿村,匯編如下
已優(yōu)化匯編
修改1:main中調(diào)用
int sum(int a, int b){
return a + b;
}
int main(int argc, char * argv[]) {
sum(1, 2);
}
查看此時(shí)是否有1锐朴,2,發(fā)現(xiàn)也沒有sum有關(guān)的匯編代碼蔼囊,其優(yōu)化原則是這樣的:去掉sum對(duì)程序運(yùn)行的結(jié)果沒有影響【原則1:對(duì)結(jié)果沒有任何影響的代碼會(huì)被編譯器優(yōu)化】
修改2:打印sum的結(jié)果
int sum(int a, int b){
return a + b;
}
int main(int argc, char * argv[]) {
int a = sum(1, 2);
printf(@"%d", a);
}
此時(shí)如果優(yōu)化掉sum焚志,對(duì)執(zhí)行結(jié)果是有影響∥饭模可以發(fā)現(xiàn)sum函數(shù)被優(yōu)化掉了酱酬,優(yōu)化成了一個(gè)結(jié)果
指針
在OC中,無論參數(shù)還是返回值云矫,如果傳入/返回的是對(duì)象膳沽,這些對(duì)象都是指針,下面來看看指針的反匯編
void func(){
int *a;
printf("%lu", sizeof(a)); //sizeof不是函數(shù)让禀,是一個(gè)符號(hào)
}
int main(int argc, char * argv[]) {
func();
}
分析以上代碼的匯編代碼
指針常識(shí)
指針自增自減運(yùn)算
void func(){
int *a;
a = (int *)100;
a++;
printf("%d", a);
}
<!--打印結(jié)果-->
104
-
問題:a++之后挑社,a是多少?
- 是104巡揍,因?yàn)?code>指針的自增自減和指向的數(shù)據(jù)類型寬度有關(guān)痛阻,即因?yàn)閍指向的數(shù)據(jù)是
int
,int的寬度是4個(gè)字節(jié)
- 是104巡揍,因?yàn)?code>指針的自增自減和指向的數(shù)據(jù)類型寬度有關(guān)痛阻,即因?yàn)閍指向的數(shù)據(jù)是
-
修改1腮敌、如果將int改成char阱当,結(jié)果是多少呢俏扩?
- 因?yàn)閍指向的數(shù)據(jù)是
char
,char的寬度是1個(gè)字節(jié)
- 因?yàn)閍指向的數(shù)據(jù)是
void func(){
char *a;
a = (char *)100;
a++;
printf("%d", a);
}
<!--打印結(jié)果-->
101
-
修改2:如果int改成int呢弊添,結(jié)果是多少录淡?*
- 因?yàn)閍指向的數(shù)據(jù)是
int*
,是一個(gè)指針油坝,指針的寬度是8個(gè)字節(jié)
- 因?yàn)閍指向的數(shù)據(jù)是
void func(){
int **a;
a = (int **)100;
a++;//指針的自增自減和執(zhí)行的數(shù)據(jù)類型寬度有關(guān)
printf("%d", a);
}
<!--打印結(jié)果-->
108
-
修改3:如果將 a++ 改成 a = a + 1 呢嫉戚?
自增自減和編譯器有關(guān)!
- 注:這里的+1免钻,加的是一個(gè)步長(zhǎng)彼水,而a的步長(zhǎng)是(int*)即8個(gè)字節(jié)
void func(){
int **a;
a = (int **)100;
// a++;//指針的自增自減和執(zhí)行的數(shù)據(jù)類型寬度有關(guān)
a = a + 1;
printf("%d", a);
}
<!--打印結(jié)果-->
108
指針和指針求差值
有以下代碼,兩個(gè)指針相減的結(jié)果是多少极舔?
void func(){
int *a;
a = (int *)100;
int *b;
b = (int *)200;
int x = a - b;
printf("%d", x);
}
<!--打印結(jié)果-->
-25
因?yàn)?(100-200)/int的寬度 = -100/4 = -25
注:
指針的運(yùn)算單位是執(zhí)行的數(shù)據(jù)類型的寬度
-
問題:指針是否可以 if-else 比大小?
- 可以的,我們知道類型是可以相互轉(zhuǎn)換的链瓦,但是結(jié)構(gòu)體和基本類型是不能相互轉(zhuǎn)換的
- 任何類型都可以使用
&(取地址符號(hào))
取值
void func(){
int *a;
a = (int *)100;
int *b;
b = (int *)200;
if (a > b) {
printf("a > b");
}else{
printf("a <= b");
}
}
<!--打印結(jié)果-->
a <= b
指針的反匯編
定義一個(gè)指針拆魏,并賦值,查看其匯編代碼
void func(){
int *a;
int b = 10;
a = &b;
}
以下是代碼運(yùn)行的匯編以及分析
指針數(shù)組
問題1:通過下面這種方式能否將數(shù)組中的數(shù)組正確取出慈俯?
void func(){
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5 ; i++) {
// printf("%d", arr[i]);
printf("%d",*(arr + i));//這里使用arr++不可以渤刃,因?yàn)榫幾g器不允許
}
}
運(yùn)行發(fā)現(xiàn)是可以的,所以有以下對(duì)等關(guān)系:
- 對(duì)等關(guān)系:
int *a == arr[0] == arr
問題2:如果將arr賦值給指針a贴膘,for循環(huán)中 a++是否可以卖子?
void func(){
int arr[5] = {1, 2, 3, 4, 5};
int *a = arr;
for (int i = 0; i < 5 ; i++) {
// printf("%d", arr[i]);
printf("%d",*(a++));
}
}
運(yùn)行發(fā)現(xiàn)也是可以的
指針的基本用法
- 定義一個(gè)指針,并從指針中取值
void func(){
char *p1;
char c = *p1;//取p1的值
}
運(yùn)行發(fā)現(xiàn)刑峡,崩潰報(bào)錯(cuò):原因是以0作為地址取值洋闽,以下是對(duì)應(yīng)的匯編分析
*如果修改成 (p1+0) 的形式呢?
void func(){
char *p1;
char c = *p1;//取p1的值
char d = *(p1+0);
}
查看匯編代碼:
-
p1
指針指向sp+0x8棧區(qū)域里的值
突梦,相當(dāng)于p1 -> (X8)0x0
-
c
相當(dāng)于取X8的值诫舅,即[X8]
-
d
同樣的是取X8的值,即[X8]
指針的基本用法-02
*如果改成 (p1+1) 的形式呢宫患?
void func(){
char *p1;
char c = *p1;//取p1的值
char d = *(p1+1);
}
查看匯編:
-
p1
指針指向sp+0x8棧區(qū)域里的值
刊懈,相當(dāng)于p1 -> (X8)0x0
-
c
相當(dāng)于取X8的值,即[X8]
-
d
同樣的是取X8的值娃闲,即[X8+0x1]
指針的基本用法-03
如果將 char類型 改成 int類型 呢虚汛?
void func(){
int *p1;
int c = *p1;//取p1的值
int d = *(p1+1);
}
以下是func函數(shù)的匯編,此時(shí)d是取[x8+0x4]
地址的值皇帮,因?yàn)閕nt是4個(gè)字節(jié)卷哩,0x4
就是int類型的步長(zhǎng)
如果將 int 類型改成 int 類型呢?*
void func(){
int **p1;
int *c = *p1;//取p1的值
int *d = *(p1+1);
}
-
查看匯編玲献,此時(shí)
d
是取[x8+0x8]
地址的值殉疼,因?yàn)閕nt*是一個(gè)指針梯浪,占8個(gè)字節(jié),0x8
就是int*類型的步長(zhǎng)
指針的基本用法-05 int** 需要拉伸多少個(gè)字節(jié)瓢娜?
實(shí)際需要3x8=24
字節(jié)挂洛,由于是匯編是16字節(jié)對(duì)齊,所以需要sub減0x20-
此時(shí)多增加一個(gè)
int* p2
眠砾,椔簿ⅲ空間拉伸多少字節(jié)?
發(fā)現(xiàn)仍然是0x20
指針的基本用法-06 -
再多增加一個(gè)
char c1
呢褒颈?
此時(shí)超過了32柒巫,所以需要再多拉伸16字節(jié)
指針的基本用法-07
多級(jí)指針
1、二級(jí)指針
有以下代碼
void func(){
int **p1;
int c = **p1;
}
運(yùn)行崩潰谷丸,查看其匯編
x8
取的是*p1
(即一級(jí)指針的地址)的值-
w9
取的是**p1
(即二級(jí)指針的地址) 的值
多級(jí)指針-01
2堡掏、多級(jí)指針加法運(yùn)算
void func(){
char **p1;
// char c = p1+2; //此時(shí)的+2是 +0x10(執(zhí)行數(shù)據(jù)類型是char*)
char c = *(*(p1 + 2) + 2);//最外層的+2,是加0x2(執(zhí)行數(shù)據(jù)類型是char)
}
此時(shí)c中的
+2
是+0x10
(執(zhí)行數(shù)據(jù)類型是char*
)此時(shí)d中
最外層的+2
刨疼,是加0x2
(執(zhí)行數(shù)據(jù)類型是char
)
如果是下面這種形式呢泉唁?
void func(){
char **p1;
// char c = p1+2; //此時(shí)的+2是 +0x10(執(zhí)行數(shù)據(jù)類型是char*)
// char c = *(*(p1 + 2) + 2);//最外層的+2,是加0x2(執(zhí)行數(shù)據(jù)類型是char)
char c2 = p1[1][2]; //與上面等價(jià)
}
p1[1]
此時(shí)的1表示0x8
(類型是char*)-
p1[1][2]
此時(shí)的2表示0x2
(類型是char)
多級(jí)指針-02
從匯編結(jié)果來看p1[1][2]
與*(*(p1 + 2) + 2)
是等價(jià)的
總結(jié)
-
編譯器優(yōu)化:
1揩慕、設(shè)置:
BuildSetting->Optimization Level
2亭畜、優(yōu)化原則:對(duì)結(jié)果沒有任何影響的代碼會(huì)被編譯器優(yōu)化
3、編譯器優(yōu)化迎卤,本質(zhì)是LLVM的優(yōu)化過程拴鸵,實(shí)際上優(yōu)化的是匯編代碼(可以理解為
匯編指令會(huì)減少
)
-
指針:
1、指針的
自增自減
和指向的數(shù)據(jù)類型寬度
有關(guān)蜗搔,是按照指向的數(shù)據(jù)類型來運(yùn)算的(即指針的寬度 - 步長(zhǎng)
)2劲藐、指針的
運(yùn)算單位
是指向的數(shù)據(jù)類型的寬度
3、指針可以通過
if-else
比大小碍扔,因?yàn)轭愋褪强梢韵嗷マD(zhuǎn)換的