C語言,開發的基礎功底,iOS很多高級應用都要和C語言打交道,所以,C語言在iOS開發中的重要性,你懂的。里面的一些問題可能并不是C語言問題,但是屬于計算機的一些原理性的知識點,所以我就不再另外寫一篇文章了,直接寫在這里。
當你寫下面的代碼時會發生什么事?
- least = MIN(*p++, b);
- 結果是:((p++) <= (b) ? (p++) : (*p++)) 這個表達式會產生副作用,指針p會作三次++自增操作。
用預處理指令#define聲明一個常數,用以表明1年中有多少秒(忽略閏年問題)
define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL(UL無符號長整形)
寫一個"標準"宏MIN ,這個宏輸入兩個參數并返回較小的一個。
define MIN(A,B) ((A) <= (B) ? (A) : (B))
寫一個標準宏Max,并給出以下代碼的輸出
int array[5] = {1, 2, 3, 4, 5};
int *p = &array[0];
int max = Max(*p++, 1);
printf("%d %d", max, *p);
參考答案: 1,2
#define Max(X, Y) ((X) > (Y) ? (X) : (Y))
當看到宏時,就會想到宏定義所帶來的副作用。對于++、–,在宏當中使用是最容易產生副作用的,因此要慎用。
分析:
p指針指向了數組array的首地址,也就是第一個元素對應的地址,其值為1.
宏定義時一定要注意每個地方要加上圓括號
*p++相當于*p, p++,所以Max(*p++, 1)相當于:
(*p++) > (1) ? (*p++) : (1)
=>
(1) > (1) ? (*p++) : (1)
=>
第一個*p++的結果是,p所指向的值變成了2,但是1 > 1為値,所以最終max的值就是1。而后面的(*p++)也就不會執行,因此p所指向的地址對應的值就是2,而不是3.
擴展:如果上面的*p++改成*(++p)如何?
(*++p) > (1) ? (*++p) : (1)
=>
(2) > (1) ? (*++p) : (1)
=>
max = *++p;
=>
*p = 3,max = 3;
define定義的宏和const定義的常量有什么區別?
λ #define定義宏的指令,程序在預處理階段將用#define所定義的內容只是進行了替換。因此程序運行時,常量表中并沒有用#define所定義的宏,系統并不為它分配內存,而且在編譯時不會檢查數據類型,出錯的概率要大一些。
λ const定義的常量,在程序運行時是存放在常量表中,系統會為它分配內存,而且在編譯時會進行類型檢查。
#define定義表達式時要注意“邊緣效應”,例如如下定義:
#define N 2 + 3 // 我們預想的N值是5,我們這樣使用N
int a = N / 2; // 我們預想的a的值是2.5,可實際上a的值是3.5
關鍵字volatile有什么含意?并給出三個不同的例子
- 優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
- 并行設備的硬件寄存器(如:狀態寄存器)
- 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
- 多線程應用中被幾個任務共享的變量
完成字符串拷貝可以使用sprintf、strcpy、以及memcpy函數,請問這些函數有什么區別?你喜歡哪一個?為什么?
這些函數的區別在于實現功能以及操作對象不同。
- strcpy:函數操作的對象是字符串,完成從源字符串到目的字符串的拷貝功能。
- sprintf:這個函數主要用來實現(字符串或基本數據類型)向字符串的轉換功能。如果源對象是字符串,并且指定%s格式符,也可實現字符串拷貝功能。
- memcpy:函數顧名思義就是內存拷貝,實現將一個內存塊的內容復制到另一個內存塊這一功能。內存塊由其首地址以及長度確定。因此,memcpy 的操作對象適用于任意數據類型,只要能給出對象的起始地址和內存長度信息、并且對象具有可操作性即可。鑒于memcpy函數等長拷貝的特點以及數據類型代表的物理意義,memcpy函數通常限于同種類型數據或對象之間的拷貝,其中當然也包括字符串拷貝以及基本數據類型的拷貝。
- 對于字符串拷貝來說,用上述三個函數都可以實現,但是其實現的效率和使用的方便程度不同:
- strcpy 無疑是最合適的選擇:效率高且調用方便。
- snprintf 要額外指定格式符并且進行格式轉化,麻煩且效率不高。
- memcpy 雖然高效,但是需要額外提供拷貝的內存長度這一參數,易錯且使用不便;并且如果長度指定過大的話(最優長度是源字符串長度 + 1),還會帶來性能的下降。其實 strcpy 函數一般是在內部調用 memcpy函數或者用匯編直接實現的,以達到高效的目的。因此,使用 memcpy 和 strcpy 拷貝字符串在性能上應該沒有什么大的差別。
- 對于非字符串類型的數據的復制來說,strcpy和snprintf一般就無能為力了,可是對memcpy卻沒有什么影響。但是,對于基本數據類型來說,盡管可以用 memcpy 進行拷貝,由于有賦值運算符可以方便且高效地進行同種或兼容類型的數據之間的拷貝,所以這種情況下memcpy幾乎不被使用。memcpy的長處是用來實現(通常是內部實現居多)對結構或者數組的拷貝,其目的是或者高效,或者使用方便,甚或兩者兼有。
sprintf,strcpy,memcpy使用上有什么要注意的地方
strcpy是一個字符串拷貝的函數,它的函數原型為strcpy(char *dst, const char *src);
將src開始的一段字符串拷貝到dst開始的內存中去,結束的標志符號為 '\0',由于拷貝的長度不是由我們自己控制的,所以這個字符串拷貝很容易出錯。
具備字符串拷貝功能的函數有memcpy,這是一個內存拷貝函數,它的函數原型為memcpy(char dst, const char src, unsigned int len);將長度為len的一段內存,從src拷貝到dst中去,這個函數的長度可控。但是會有內存讀寫錯誤。(比如len的長度大于要拷貝的空間或目的空間)
sprintf是格式化函數。將一段數據通過特定的格式,格式化到一個字符串緩沖區中去。sprintf格式化的函數的長度不可控,有可能格式化后的字符串會超出緩沖區的大小,造成溢出。
static關鍵字的作用
- 隱藏。編譯多個文件時,所有未加static前綴的全局變量和函數都全局可見。
- 保持變量內容的持久。全局變量和static變量都存儲在靜態存儲區,程序開始運行就初始化,只初始化一次。static控制了變量的作用范圍。
- 默認初始化為0.在靜態數據區,內存中的所有字節都是0x00,全局變量和static變量都是默認初始化為0.
static關鍵字區別:
- static全局變量與普通的全局變量有什么區別:static全局變量只初使化一次,防止在其他文件單元中被引用;
- static局部變量和普通局部變量有什么區別:static局部變量只被初始化一次,下一次依據上一次結果值;
- static函數與普通函數有什么區別:static函數在內存中只有一份,普通函數在每個被調用中維持一份拷貝
關鍵字const
- const int a;int const a; 作用是一樣:a 是一個常整型數
- const int *a;int const *a; a 是一個指向常整型數的指針(整型數是不可修改的,但指針可以)
- int * const a;a 是一個指向整型數的常指針(指針指向的整型數是可以修改的,但指針是不可修改的)
- int const * const a;a 是一個指向常整型數的常指針(指針指向的整型數是不可修改的,同時指針也是不可修改的)
堆棧
管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產生內存泄露 (memory leak)。
-
申請大小:
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在Windows下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示 overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
碎片問題:
對于堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個 問題,因為棧是先進后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個內存塊從棧中間彈出分配方式:
堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。動態分配由 alloc函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。分配效率:
棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的 效率比較高。堆則是C/C++函數庫提供的,它的機制是很復雜的
數組和指針的區別
- 數組可以申請在棧區和數據區;指針可以指向任意類型的內存塊
sizeof作用于數組時,得到的是數組所占的內存大小;作用于指針時,得到的都是4個字節的大小 - 數組名表示數組首地址,是常量指針,不可修改指向。比如不可以將++作用于數組名上;普通指針的值可以改變,比如可將++作用于指針上
- 用字符串初始化字符數組是將字符串的內容拷貝到字符數組中;用字符串初始化字符指針是將字符串的首地址賦給指針,也就是指針指向了該字符串
引用和指針的區別
指針指向一塊內存,內容存儲所指內存的地址。
引用是某塊內存的別名。
引用使用時不需要解引用(*)而指針需要
引用只在定義時被初始化,之后不可變,指針可變。
引用沒有const
引用不能為空
sizeof引用得到的是所指向變量(對象)的大小,sizeof指針是指針本身的大小。
指針和引用的自增(++)運算意義不一樣:引用++為引用對象自己++,指針++是指向對象后面的內存
程序需要為指針分配內存區域,引用不需要。
用變量a給出下面的定義
一個整型數(An integer)
一個指向整型數的指針( A pointer to an integer)
一個指向指針的的指針,它指向的指針是指向一個整型數( A pointer to a pointer to an intege)r
一個有10個整型數的數組( An array of 10 integers)
一個有10個指針的數組,該指針是指向一個整型數的。(An array of 10 pointers to integers)
一個指向有10個整型數數組的指針( A pointer to an array of 10 integers)
一個指向函數的指針,該函數有一個整型參數并返回一個整型數(A pointer to a function that takes an integer as an argument
and returns an integer)一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數并返回一個整型數( An array of ten pointers to functions t
hat take an integer argument and return an integer )答案是:
int a; // An integer
int *a; // A pointer to an integer
int **a; // A pointer to a pointer to an integer
int a[10]; // An array of 10 integers
int *a[10]; // An array of 10 pointers to integers
int (*a)[10]; // A pointer to an array of 10 integers
int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
請寫出以下代碼輸出
int a[5] = {1, 2, 3, 4, 5};
int *ptr = (int *)(&a + 1);
printf("%d, %d", *(a + 1), *(ptr + 1));
參考答案: 2, 隨機值
這種類型題好像挺常見的。考的就是C語言上的指針的理解和數組的理解。
分析:
a代表有5個元素的數組的首地址,a[5]的元素分別是1,2,3,4,5。接下來,a + 1表示數據首地址加1,那么就是a[1],也就是對應于值為2.但是,這里是&a + 1,因為a代表的是整個數組,它的空間大小為5 * sizeof(int),因此&a + 1就是a+5。a是個常量指針,指向當前數組的首地址,指針+1就是移動sizeof(int)個字節。
因此,ptr是指向int *類型的指針,而ptr指向的就是a + 5,那么ptr + 1也相當于a + 6,所以最后的*(ptr + 1)就是一個隨機值了。而*(ptr – 1)就相當于a + 4,對應的值就是5。
簡述內存分區情況
- 代碼區:存放函數二進制代碼
- 數據區:系統運行時申請內存并初始化,系統退出時由系統釋放,存放全局變量、靜態變量、常量
- 堆區:通過malloc等函數或new等操作符動態申請得到,需程序員手動申請和釋放
- 棧區:函數模塊內申請,函數結束時由系統自動釋放,存放局部變量、函數參數
用NSLog函數輸出一個浮點類型,結果四舍五入,并保留一位小數
float money = 1.011;
NSLog(@"%.1f", money);
文章如有問題,請留言,我將及時更正。
滿地打滾賣萌求贊,如果本文幫助到你,輕點下方的紅心,給作者君增加更新的動力。