知識點總結(jié)中,如有幸被您觀看,更有可能看到不足,期待指出交流
前言
C語言,開發(fā)的基礎(chǔ)功底,iOS很多高級應(yīng)用都要和C語言打交道.所以,C語言在iOS開發(fā)中的重要性是很厲害的.現(xiàn)在過來回顧這方面的問題.
查看下面的代碼會發(fā)生什么
least = MIN(*p++, b)
結(jié)果:((* p++) <= b) ? (* p++) :b),這個會產(chǎn)生副作用,指針p會做倆次++的自增操作
用預(yù)處理指令#define聲明一個常數(shù),用來表達一年中有多少秒(忽略閏年)
define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL(UL無符號長整型)
寫一個標(biāo)準(zhǔn)宏 MIN ,這個宏輸入倆個參數(shù)并返回較小的一個
define MIN(A,B) ((A) < (B) ? (A): (B))
給出以下代碼的輸出
int array[5] = {1,2,3,4,5};
int *p = &array[0];
int max = Max(*p++, 1);
printf("%d %d", max, *p);
答案:輸出的答案,1,2.
解析:看到宏的時候,就會想到宏的副作用,對于++、--,在宏的使用中會產(chǎn)生一系列的副作用,因此要慎用.下面是其宏的結(jié)構(gòu)
#define Max(X,Y) ((X)>(Y)?(X):(Y))
(*p++) > (1) ? (*p++) :(1)
先比較(1)>(1)>(p++):(1),然后p++了,這個時候p已經(jīng)指向了2,max為1,*p為2
define定義的宏和const定義的常量有什么區(qū)別
- .#define定義宏的指令,程序在預(yù)處理階段將#define所定義的內(nèi)容只是進行了替換.所以在程序運行的時候,常量表中并沒有#define所定義的宏,系統(tǒng)不為他分配內(nèi)存.
- .#define定義的宏指令,編譯的時候不會檢查數(shù)據(jù)類型,出錯的概率會更大
- const定義的常量, 在程序運行的時候是存放在常量表中的,系統(tǒng)會給它分配內(nèi)存,編譯的時候會進行檢查
注意: .#define定義表達式時要注意邊緣效應(yīng)例如
#define N 2 + 3 // 我們預(yù)想的值是5,
int a = N / 2; // 結(jié)果就是 2 + 3 / 2 = 2.5,
關(guān)鍵字volatile有什么含義,舉出三個不同例子
優(yōu)化器在用到這個變量時必須每次都要小心的重新讀取這個變量的值,而不是使用保存在寄存器里面的備份.下面是例子
- 并行設(shè)備的硬件寄存器(如:狀態(tài)氣純器)
- 一個終端服務(wù)字程勛中會訪問到的非自動變量(Non-automatic variables)
- 多線程應(yīng)用中唄幾個任務(wù)共享的變量
完成字符拷貝可以使用sprintf空猜、strcpy绽慈、以及memcpy函數(shù),這些有什么區(qū)別
這些函數(shù)的區(qū)別在于實現(xiàn)功能以及操作的對象不同
- strcpy: 函數(shù)操作的對象是字符串,完成從源字符串到目的字符串的拷貝功能.
- sprintf:這個函數(shù)主要用來實現(xiàn)(字符串或基本數(shù)據(jù)類型)向字符串的轉(zhuǎn)換功能.如果元對象是字符串,并且制定%s格式符,也可實現(xiàn)字符串拷貝功能.
- memcpy: 函數(shù)顧名思義就是內(nèi)存拷貝,實現(xiàn)建一個內(nèi)存塊的內(nèi)容復(fù)制到另一個內(nèi)存塊這個功能.類存款由對象的其實地址和內(nèi)存長度信息,并且對象具有可操作性即可.鑒于memcpy函數(shù)的長拷貝的特點以及數(shù)據(jù)類型代表的物理意義,memcpy函數(shù)通常限于同種類型數(shù)據(jù)或?qū)ο笾g的拷貝,其中當(dāng)然也包括字符串拷貝以及基本數(shù)據(jù)類型的拷貝.
對于拷貝字符串,上述的三個函數(shù)都是可以實現(xiàn)的但是實現(xiàn)的效率和使用的方便程度不一樣 - strcpy 最合適的選擇,效率高切調(diào)用方便
- snprintf 要額外指定格式符并且進行格式轉(zhuǎn)換,麻煩而且不高效
- memcpy 雖然高效, 但是需要額外提供拷貝的內(nèi)存長度這一個參數(shù),易錯而且使用不方便.并且長度過道,還會帶來性能的下降.
- 對于非字符串的賦值strcpy和 snprintf 就不行了.這個時候就只能用 memcpy來拷貝了
static關(guān)鍵字的作用 - 隱藏,編譯多個文件時,所有未加static前綴的全局變量和函數(shù)都全局可見
- 保持變量內(nèi)容的持久.全局變量和static變量都純純在靜態(tài) 存儲區(qū),程序開始運行就初始化,只初始化一次,static控制變量的作用范圍.
- 默認(rèn)初始化為0,在靜態(tài)數(shù)據(jù)區(qū),內(nèi)存中的所有直接都是0x00,全局變量和static變量都是默認(rèn)初始化0
static的特別
- static全局變量和普通的全局變量有什么區(qū)別:static全局變量只初始化一次,防止在其他文件單元中被應(yīng)用
- static局部比啊年和普通局部變量有什么區(qū)別:static局部變量只被初始化一次,洗一次依據(jù)上一次的結(jié)果值,
- static函數(shù)和普通函數(shù)有什么區(qū)別:static函數(shù)在內(nèi)存中只有一份,普通函數(shù)在每個被調(diào)用中維持一份拷貝
想看的具體一點可以看下面的文章
Static關(guān)鍵字理解(iOS)
關(guān)鍵字const - const int a; int const a; 作用一樣, a是一個長整型數(shù)
- const int a; int const a;a是一個紙箱長整數(shù)的指針(整型數(shù)是不可修改的,但是指針可以)
- int * const a; a 是一個紙箱整型數(shù)的常指針(真正指向的整型數(shù)是可以修改的,但指針是不可以修改的)
- int const *const a; a 是一個紙箱常整數(shù)的常指針(指針指向的整型數(shù)是不可修改的, 同事指針也是不可修改的)
Const 關(guān)鍵字理解(iOS)
堆棧
- 管理方式:對于棧來說,是有編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作是有程序員控制,容易產(chǎn)生內(nèi)存泄漏.
- 申請大小:
-- 棧:在windows下,棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域.這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在windows下,棧的大小是2M(也有的說是1M,總之是編譯時就確定的參數(shù)),如果申請的空間超過棧剩余空間時,就會提示 overflow.因此能從棧獲得的空間小
-- 堆:堆是向高地址擴展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域.這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的,自然是不連續(xù)的,二鏈表的遍歷方向是由低地址向高地址.堆的大小受限于計算機系統(tǒng)中有效的虛擬內(nèi)存.由此可見,堆獲取的空間比較活,也比較大. - 碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率低下,對于棧來講,就不會存在這個問題,因為棧是先進后出的隊列,他們是如此的一一對應(yīng),以至于永遠(yuǎn)都不可能有一塊內(nèi)存塊從棧中彈出
- 分配方式:堆都是動態(tài)分配,沒有靜態(tài)分配的堆.棧有倆種分配方式:靜態(tài)分配和動態(tài)分配.靜態(tài)分配是編譯器完成的,比如局部變量的分配.動態(tài)分配由alloc函數(shù)進行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編輯器進行釋放,無需我們手工實現(xiàn).
- 分配效率:棧是及其系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機會在底層對棧提供支持:分配專門的寄存器存在放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高.堆則是c/c++函數(shù)庫提供的,他的機制很復(fù)雜的.
數(shù)組和指針的區(qū)別
- 數(shù)組可以申請在棧區(qū)和數(shù)據(jù)區(qū):指針可以指向任意類型的內(nèi)存塊,sizeof作用于數(shù)組時,得到的是數(shù)組所占的內(nèi)存大小,作用于指針時,得到的都是4個字節(jié)的大小.
- 數(shù)組名標(biāo)識數(shù)組的首地址,是常量指針,不可修改指向.比如不可以將++作用于數(shù)組名上;普通指針的值可以改變,比如可將++作用于指針上
- 用字符串初始化字符數(shù)組是將字符串的內(nèi)容拷貝到字符數(shù)組中;用字符串初始化字符指針是將字符串的首地址賦值給指針,也就是指針指向了該字符串
引用和指針的區(qū)別
- 指針指向一塊內(nèi)存, 內(nèi)存儲存所指向內(nèi)存的地址
- 引用是某塊內(nèi)存的別名
- 引用時不需要解引用(*)而指針需要
- 引用只在定義時唄初始化,之后不可變,指針可變
- 引用沒有const
- 引用不能為空
- sizof引用得到的是所指向變量(對象)的大小,sizeof指針是指針本身的大小
- 指針和引用的自增(++)運算意義不一樣:引用++未引用對象自己++.指針++是真想對象后面的內(nèi)存.
- 程序需要尾指針分配內(nèi)存區(qū)域,引用不需要
用變量a給出下面的定義
- (1)一個整型數(shù)
- (2)一個指向整型數(shù)的指針
- (3)一個指向指針的指針,它指向指針是指向一個整型數(shù)
- (4)一個有10個整型數(shù)的數(shù)組
- (5)一個有10個指針的數(shù)組,該指針是指向一個整型數(shù)的
- (6)一個指向有10個整型數(shù)數(shù)組的指針
- (7)一個指向函數(shù)的指針,該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)
- (8)一個有10個指針的數(shù)組,該指針指向一個函數(shù),該函數(shù)有一個整型參數(shù)并返回一個整數(shù)型
(1)int a;
(2)int *a;
(3)int **a;
(4)int a[10]
(5)int *a[10]
(6)int (*)a[10]
(7)int (*)a(int)
(8)int (*a[10])(int)
請寫出以下代碼輸出
int a[5] = {1,2,3,4,5}
int *ptr = (int *)(&a+1);
printf("%d, %d", *(a+1), *(ptr + 1));
參考答案:2,隨機值
分析:a代表有5個元素的數(shù)組的首地址,a[5]的元素分別是1,2,3,4,5.接下來,a+1表示數(shù)據(jù)首地址加1,那么就是a[1],對應(yīng)的值也就是2.但是,這里是&a+1,因為a代表的是整個數(shù)組,他的空間大小為5sizeof(int),因此&a+1就是a+5.a是個常量指針,指向當(dāng)前數(shù)組的首地址,指針+1就是移動sizeof(int)個字節(jié).因此,ptr是指向int 類型的指針,而ptr 指向的就是a+5,那么ptr+1也就相當(dāng)于a + 6,所有最后一個(ptr + 1)就是一個隨機值了.而(ptr-1)就相當(dāng)于a+4,對應(yīng)的值就是5.
簡述內(nèi)存分區(qū)情況
- 代碼區(qū): 存放函數(shù)二進制代碼
- 數(shù)據(jù)區(qū): 系統(tǒng)運行時申請內(nèi)存并初始化,系統(tǒng)推出時由系統(tǒng)示范,存放全區(qū)變量,靜態(tài)變量,常量
- 堆區(qū):通過malloc等函數(shù)或new等操作浮動動態(tài)申請得到,需要程序員手動申請和釋放.
- 棧區(qū):函數(shù)模塊內(nèi)申請,函數(shù)結(jié)束時由系統(tǒng)自動釋放,存放局部變量,函數(shù)參數(shù)
用NSLog函數(shù)輸出一個浮點類型,結(jié)果四舍五入,并保留一位小數(shù)
float money = 1.011;
NSLog(@"%.1f",money);